I have a clojure app that will share with some people. And there are username password pre-defined in the clojure. If some guy get the app and decompile it, he might see the password. My only intention is to hidden the password/username. what's the simple way to do it. I created the jar file using
lein uberjar
and then send the standalone jar file as client code.
You cannot prevent decompilation, you can only obfuscate it. Depending on your security requirements, this may be adequate. Otherwise, you should really look at moving those sensitive username and password calls into an authenticated service that you control. If you update the question to give more info, we might be able to give more specific recomendations.
If you can't rely on an external service (no internet connection), you can store the hash of the password in a file of your uberjar.
; utility methods
(defn to-base64 [bytes]
(String. (.encode (java.util.Base64/getEncoder) bytes)))
; function to encrypt string formatted password
(defn encrypt-password [string]
(let [ sha (java.security.MessageDigest/getInstance "SHA")]
(.update sha (.getBytes string))
(to-base64
(.digest sha))))
; call this one time, to store the secret in encrypted form
; this would be part of your jar file, but regular users cannot
; (probably) decrypt this.
(defn save-password [ secret-password ]
(spit
"secret"
(encrypt-password secret-password)))
; use this to validate this
(defn validate-password [ input ]
(.equalsIgnoreCase
(String. (slurp "secret"))
(String. (encrypt-password input))))
Finally, you can create and check passwords with the above methods:
(save-password "hello")
(validate-password "hello")
; true
(save-password "hellome!")
(validate-password "hello")
; false
Related
I set up a basic luminus template with postgres. I want to be able to add new users and authenticate them, following an example from Ch.6 p.168 in this book: https://pragprog.com/titles/dswdcloj3/web-development-with-clojure-third-edition/
I had issues, so I started over with a new project to just try to get through this part.
I created a new luminus project, I created the database table, and I updated the project.clj file. I'm getting an error, when I try to create a test user.
I have done the following commands:
lein new luminus testproject --template-version 3.91 -- +postgres +http-kit
cd testproject
lein repl
in another terminal, I do
psql
CREATE USER testproject WITH PASSWORD 'password';
CREATE DATABASE testproject WITH OWNER testproject;
I updated the dev_config.edn file:
{:dev true
:port 3000
:nrepl-port 7000
:database-url "postgresql://localhost:5432/testproject?user=testproject&password=password"
}
Then I did
lein repl
(start)
(create-migration "add-users-table")
(migrate)
(in-ns 'testproject)
(create-user! "testuser" "testpass")
I get this error:
Unable to resolve symbol: create-user! in this context
files I edited/created:
project.clj (added buddy)
auth.clj:
(ns testproject.core
(:require
[buddy.hashers :as hashers]
[next.jdbc :as jdbc]
[testproject.db.core :as db]))
(defn create-user! [login password]
(jdbc/with-transaction [t-conn db/*db*]
(if-not (empty? (db/get-user-for-auth* t-conn {:login login}))
(throw (ex-info "User already exists!"
{:testproject/error-id ::duplicate-user
:error "User already exists!"}))
(db/create-user!* t-conn
{:login login
:password (hashers/derive password)}))))
(defn authenticate-user [login password]
(let [{hashed :password :as user} (db/get-user-for-auth* {:login login})]
(when (hashers/check password hashed)
(dissoc user :password))))
queries.sql:
-- :name create-user!* :! :n
-- :doc creates a new user with the provided login and hashed password
INSERT INTO users
(login, password)
VALUES (:login, :password)
-- :name get-user-for-auth* :? :1
-- :doc selects a user for authentication
SELECT * FROM users
WHERE login = :login
migrations:
add users table up:
CREATE TABLE users
(login text PRIMARY KEY,
password text not null,
created_at TIMESTAMP not null DEFAULT now());
add users table down:
DROP TABLE users;
I was able to add messages/posts just fine. For this testproject, I just am trying to do the new users to isolate the issue.
What am I doing wrong? What parts should I pay attention to?
Thanks.
You must require a namespace to load it before you use in-ns -- otherwise you're just going to navigate into an empty namespace with no functions in (and not even the clojure.core functions referred into it!).
Thanks for the help everyone. I had a typo. My function is in the auth.clj file, which I labeled (ns testproject.core ...), so that was a big error. I changed it to testproject.auth, then required that namespace, and then was able to use the function correctly.
Try this:
(in-ns 'testproject.core)
Every part of the namespace symbol is important.
I am new to re-frame and not quite sure how to build a user authentication/authorization system with it.
From what I gathered I should create an auth interceptor and place my auth logic inside :before section then inject the interceptor into every events reg-event-db and reg-event-fx that I want to protect.
Am I on the right track?
Not sure if my solution is particularly idiomatic, but I used something like the following in one of my projects. Consider it a Works For Me.
Create a map for the ajax request with a special value for error cases (ignore the context-uri function):
(defn xhrio-map [method path format success-event]
{:method method
:uri (context-uri path)
:timeout 5000
:response-format format
:on-success [success-event]
:on-failure [::ajax-failure]})
Then I use an fx handler for the failure (this is a bit more complicated as it also handles a loading indicator):
(rf/reg-event-fx
::ajax-failure
(fn [{:keys [db]} [_ http-result]]
(if (= 403 (:status http-result))
{:db (assoc db :loading-indicator nil)
:dispatch [::logout]}
{:db (assoc db :loading-indicator nil)
:dispatch
[::error-msg (str "Error fetching from " (:uri http-result)
": " (:response http-result))]})))
The ::logout events sets the document location. This also triggers the logout in the backend.
(rf/reg-event-fx
::logout
(fn [coefx [ev]]
{::location "./logout"}))
Finally, the loading of resources works like this:
(defn load-with-indicator [db xhrio-data]
{:db (assoc db :loading-indicator true)
:http-xhrio xhrio-data})
(rf/reg-event-fx
::load-documentation
(fn [{:keys [db]} _]
(load-with-indicator
db
(xhrio-map :get "documentation/"
(ajax/json-response-format {:keywords? true})
::received-documentation))))
The :received-documentation is handled by some code which invokes the correct display functions.
This uses the day8.re-frame/http-fx and ajax.core
On the backend, I use something similar to the demo code I published over at https://github.com/ska2342/ring-routes-demo.
Hope that helps.
License of the code in this post
In addition to the default license of the StackOverflow site, I also publish these lines under the Eclipse Public License either version 1.0 or (at your option) any later version.
In using clojure-ring I'm trying to do a simple test by posting data form a form and then printing it to the browser.
(defroutes approutes
;posting test
(POST "/upload" [req]
(str "the wonderful world of wonka presents " req)))
when I try posting data via curl it gives me a 200 okay status code, but it doesn't actually fill in the body of the request with the params. Perhaps I am overlooking something fundamental about Ring.
edt:
what it does output is
the wonderful world of wonka presents
but the rest doesn't show up.
compojure's destructuring tries to access the query/form parameter :req in your example, not the whole request. You have two possibilities:
(POST "..." req ...)
and
(POST "..." [something :as req] ...)
Both store the request in req, the second variant allows you to still use destructuring , though.
I have a ring middleware which does some check on request maps with the header values.
For the check I have to hit the database.
If a defroutes as a set of routes starting with acommon URI pattern.
I don't want a middleware to run for any random URL that matches the pattern before getting handled.
I only want middleware to run for a certain set of URIs that I am suing inside of defroutes only. The reason being there is a database access in the middleware which I want to avoid for 404 responses having the same pattern.
Here is comporoute, a ring handler without any macro magic, aimed at composability and extensibility.
Even though it's in early alpha state it has precise docstrings already. It has a feature called inner middleware to solve the issue you are having. You may (and should) use it only for what you need it for and leave the rest to Compojure.
Given your Compojure handler/app is called compojure:
(defn demo-middleware
"A test midleware associng :bar to :foo of request"
[handler]
(fn [request]
(handler (assoc request :foo :bar))))
(defn demo-handler [request]
(ring.util.response/response
(str "id is " (get-in request [:params :id]) " "
":foo is" (:foo request))))
(def app
(comporoute.core/router
[["/demo-with-middleware"
[demo-middleware ;; all handlers in this vector are
;; wrapped via demo-middleware
["/:id" :demo-with demo-handler]]]
["/demo-without-middleware"
["/:id" :demo-without demo-handler]]]
:page-not-found compojure)
At the shell
curl http://localhost:8080/demo-without-middleware/1234
id is 1234 :foo is
curl http://localhost:8080/demo-with-middleware/1234
id is 1234 :foo is :bar
# Everything else will be handled by compojure.
Dependency vector [comporoute "0.2.0"]
I am beginning to use luminus framework to develop a web app, and I am trying to use friend for auth, I am stacked here, I don't know how to use that like using gem in rails app.
I don't know where should I put the code in luminus, is there anyone can show me a demo. Or tell me what to do next?
Well, you can also tell me how to write a log in function in luminus.
The login sort of works like it is posted in the Luminus Docs. Not sure if you managed to read that part, but I'll show you a simplified version of the code I use. I want to mention that I removed quite a bit of code to make everything a bit easier to understand, so this may not work as-is since I only deleted code and extra parens. Since it is from actual working code, it will work with a bit of tweeking:
The first part is getting the login form:
(defn login-page []
(html5
[:h3 "Login"]
[:form {:method "POST" :action "login"}
[:div "Username:"
[:input {:type "text" :name "username" :required "required"}]]
[:div "Password:"
[:input {:type "password" :name "password" :required "required"}]]
[:div
[:input {:type "submit" :value "Log In"}]]]]))
Notice that there is a "POST" method? In order to get the routes to work, you have to have a "POST" route, but you will also need a "GET" route. This is the simplified version of the "GET" "POST" loop that I have:
(defroutes app-routes
(GET "/login" []
(log/login-page))
(POST "/login" [username password]
(do-login username password)))
The (do-login) function is where I authenticate the user / password combo, which then sets the session, which is shown downn below.
Notice that the POST route needs arguments. The arguments must match the "name" parameters in the form.
Finally, to get it all to work, you have to hook up some sessions. I personally use lib-noir.sessions:
(ns myapp.handler
(:require [noir.session :as sesh])
Then you have to create a map to hold the session, which I'm wrapping in a function here (note that the :key must match whatever you have in your database:
(defn set-user [username]
(sesh/put! :handle username))
And finally, you have to tell clojure that you want to allow sessions to be handled via middleware:
(def app
(sesh/wrap-noir-session
(handler/site
app-routes)))
Hopefully that gives you a bit of a headstart. I did not include how to connect to a database or how to use a map, but the above should be enough to get you on your way. I also did not touch on authorization and security (please don't skip this!). Using a database, map, or friend isn't a huge quantum leap from here. Just wanted to offer just enough to get you started.
Here's an example from when I did a luminus+friend combination, granted, they've changed the template several times, so this is from an older version, but the concepts the same, I hope it helps.
(def all-routes
[home-routes cljs-routes test-routes app-routes])
(def app
(-> all-routes middleware/app-handler ))
(def secured-app
(handler/site
(friend/authenticate app{
:login-uri "/login"
:unauthorized-redirect-uri "/login"
:credential-fn (partial creds/bcrypt-credential-fn users)
:workflows [(workflows/interactive-form)]})))
(def war-handler
(middleware/war-handler secured-app))