From c0f86f6168cf57fbed813dd2e12f44ab595d8f70 Mon Sep 17 00:00:00 2001 From: Kazuki Tsutsumi Date: Thu, 20 Apr 2017 13:29:02 +0900 Subject: [PATCH 1/9] Fixed version for develop. --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index af0b7dd..5902d52 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.6 +1.1.0-SNAPSHOT From 984e83eed899dfb37e4f77807a859e0e82f57b42 Mon Sep 17 00:00:00 2001 From: miyamoen Date: Wed, 21 Jun 2017 20:37:05 +0900 Subject: [PATCH 2/9] Fix typo. roll -> role --- .../control_bus/component/auth.clj | 50 +++++++++---------- src/clj/job_streamer/control_bus/model.clj | 4 +- .../control_bus/component/auth_test.clj | 30 +++++------ 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/clj/job_streamer/control_bus/component/auth.clj b/src/clj/job_streamer/control_bus/component/auth.clj index 9884a3a..37be398 100644 --- a/src/clj/job_streamer/control_bus/component/auth.clj +++ b/src/clj/job_streamer/control_bus/component/auth.clj @@ -34,8 +34,8 @@ '{:find [?permission] :in [$ ?app-name ?user-id] :where [[?member :member/user ?user] - [?member :member/rolls ?roll] - [?roll :roll/permissions ?permission] + [?member :member/roles ?role] + [?role :role/permissions ?permission] [?user :user/id ?user-id] [?app :application/members ?member] [?app :application/name ?app-name]]} @@ -53,16 +53,16 @@ :where [[?u :user/id ?id]]} id))) -(defvalidator exist-roll-validator +(defvalidator exist-role-validator {:default-message-format "%s is used by someone."} - [roll-name datomic] + [role-name datomic] (d/query datomic '[:find ?e . :in $ ?n - :where [?e :roll/name ?n]] - roll-name)) + :where [?e :role/name ?n]] + role-name)) -(defn- signup [datomic user roll-name] +(defn- signup [datomic user role-name] (when-let [user (let [salt (nonce/random-nonce 16) password (some-> (not-empty (:user/password user)) (.getBytes) @@ -78,11 +78,11 @@ :user/salt salt}) (when-let [token (:user/token user)] {:user/token token}))))] - (let [roll-id (d/query datomic + (let [role-id (d/query datomic '[:find ?e . :in $ ?n - :where [?e :roll/name ?n]] - roll-name) + :where [?e :role/name ?n]] + role-name) member-id (d/tempid :db.part/user) app (d/query datomic '[:find (pull ?e [*]) . @@ -92,9 +92,9 @@ (let [result (d/transact datomic [user {:db/id member-id :member/user (select-keys user [:db/id]) - :member/rolls [roll-id]} + :member/roles [role-id]} (update-in (select-keys app [:db/id :application/members]) [:application/members] #(conj % member-id))])] - (log/infof "Signup %s as %s succeeded." (:user/id user) roll-name) + (log/infof "Signup %s as %s succeeded." (:user/id user) role-name) result)))) (defn auth-resource @@ -143,13 +143,13 @@ [v/min-count 3 :message "Username must be at least 3 characters long."] [v/max-count 20 :message "Username is too long."] [unique-name-validator datomic]] - :roll [[v/required] + :role [[v/required] [v/matches #"^[\w\-]+$"] - [exist-roll-validator datomic]])) + [exist-role-validator datomic]])) :post! (fn [{user :edn}] - (let [roll-name (:roll user) + (let [role-name (:role user) user (select-keys user [:user/id :user/password])] - (signup datomic user roll-name))) + (signup datomic user role-name))) :delete! (fn [_] (d/transact datomic [[:db.fn/retractEntity [:user/id user-id]]])))) @@ -159,26 +159,26 @@ (start [component] - ;; Create an initil user and rolls. + ;; Create an initil user and roles. (->> [{:db/id (d/tempid :db.part/user) - :roll/name "admin" - :roll/permissions [:permission/read-job + :role/name "admin" + :role/permissions [:permission/read-job :permission/create-job :permission/update-job :permission/delete-job :permission/execute-job]} {:db/id (d/tempid :db.part/user) - :roll/name "operator" - :roll/permissions [:permission/read-job + :role/name "operator" + :role/permissions [:permission/read-job :permission/execute-job]} {:db/id (d/tempid :db.part/user) - :roll/name "watcher" - :roll/permissions [:permission/read-job]}] + :role/name "watcher" + :role/permissions [:permission/read-job]}] (filter #(nil? (d/query datomic '[:find ?e . :in $ ?n - :where [?e :roll/name ?n]] - (:roll/name %)))) + :where [?e :role/name ?n]] + (:role/name %)))) (d/transact datomic)) (when-not (d/query datomic '[:find ?e . diff --git a/src/clj/job_streamer/control_bus/model.clj b/src/clj/job_streamer/control_bus/model.clj index 7a77b14..c24bbb5 100644 --- a/src/clj/job_streamer/control_bus/model.clj +++ b/src/clj/job_streamer/control_bus/model.clj @@ -85,14 +85,14 @@ (schema member (fields [user :ref] - [rolls :ref :many])) + [roles :ref :many])) (schema user (fields [id :string :indexed :unique-value] [password :string] [salt :bytes] [token :string])) - (schema roll + (schema role (fields [name :string :indexed :unique-value] [permissions :keyword :many]))]) diff --git a/test/clj/job_streamer/control_bus/component/auth_test.clj b/test/clj/job_streamer/control_bus/component/auth_test.clj index 0a4953e..3f70f60 100644 --- a/test/clj/job_streamer/control_bus/component/auth_test.clj +++ b/test/clj/job_streamer/control_bus/component/auth_test.clj @@ -75,7 +75,7 @@ (testing "login as created user" (let [request {:request-method :post :content-type "application/edn" - :body (pr-str {:user/id "addeduser" :user/password "password123" :roll "watcher"})} + :body (pr-str {:user/id "addeduser" :user/password "password123" :role "watcher"})} {:keys [status body]} (let [handler (auth/entry-resource (:auth system) nil)] (handler request))] (is (= 201 status))) (let [request {:request-method :post @@ -115,7 +115,7 @@ (testing "lacking id" (let [request {:request-method :post :content-type "application/edn" - :body (pr-str {:user/password "password123" :roll "watcher"})} + :body (pr-str {:user/password "password123" :role "watcher"})} {:keys [status body]} (handler request)] (are [x y] (= x y) 400 status @@ -123,23 +123,23 @@ (testing "lacking password" (let [request {:request-method :post :content-type "application/edn" - :body (pr-str {:user/id "test" :roll "watcher"})} + :body (pr-str {:user/id "test" :role "watcher"})} {:keys [status body]} (handler request)] (are [x y] (= x y) 400 status ["password must be present" "token must be present"] (-> body edn/read-string :messages)))) - (testing "lacking roll" + (testing "lacking role" (let [request {:request-method :post :content-type "application/edn" :body (pr-str {:user/id "test" :user/password "password123"})} {:keys [status body]} (handler request)] (are [x y] (= x y) 400 status - ["roll must be present"] (-> body edn/read-string :messages)))) + ["role must be present"] (-> body edn/read-string :messages)))) (testing "id does not satisfy min length" (let [request {:request-method :post :content-type "application/edn" - :body (pr-str {:user/id "te" :user/password "password123" :roll "watcher"})} + :body (pr-str {:user/id "te" :user/password "password123" :role "watcher"})} {:keys [status body]} (handler request)] (are [x y] (= x y) 400 status @@ -147,7 +147,7 @@ (testing "id does not satisfy max length" (let [request {:request-method :post :content-type "application/edn" - :body (pr-str {:user/id "test12345678901234567" :user/password "password123" :roll "watcher"})} + :body (pr-str {:user/id "test12345678901234567" :user/password "password123" :role "watcher"})} {:keys [status body]} (handler request)] (are [x y] (= x y) 400 status @@ -155,7 +155,7 @@ (testing "password does not satisfy min length" (let [request {:request-method :post :content-type "application/edn" - :body (pr-str {:user/id "test" :user/password "passwor" :roll "watcher"})} + :body (pr-str {:user/id "test" :user/password "passwor" :role "watcher"})} {:keys [status body]} (handler request)] (are [x y] (= x y) 400 status @@ -163,23 +163,23 @@ (testing "id conflicts" (let [request {:request-method :post :content-type "application/edn" - :body (pr-str {:user/id "admin" :user/password "password123" :roll "watcher"})} + :body (pr-str {:user/id "admin" :user/password "password123" :role "watcher"})} {:keys [status body]} (handler request)] (are [x y] (= x y) 400 status ["id is used by someone."] (-> body edn/read-string :messages)))) - (testing "invalid roll" + (testing "invalid role" (let [request {:request-method :post :content-type "application/edn" - :body (pr-str {:user/id "test" :user/password "password123" :roll "nothing"})} + :body (pr-str {:user/id "test" :user/password "password123" :role "nothing"})} {:keys [status body]} (handler request)] (are [x y] (= x y) 400 status - ["roll is used by someone."] (-> body edn/read-string :messages)))) + ["role is used by someone."] (-> body edn/read-string :messages)))) (testing "create user" (let [request {:request-method :post :content-type "application/edn" - :body (pr-str {:user/id "test" :user/password "password123" :roll "watcher"})} + :body (pr-str {:user/id "test" :user/password "password123" :role "watcher"})} {:keys [status body]} (handler request)] (is (= 201 status))) (let [handler (auth/list-resource (:auth system)) @@ -193,12 +193,12 @@ (testing "delete user" (let [request {:request-method :post :content-type "application/edn" - :body (pr-str {:user/id "test" :user/password "password123" :roll "watcher"})} + :body (pr-str {:user/id "test" :user/password "password123" :role "watcher"})} {:keys [status body]} (handler request)] (is (= 201 status))) (let [request {:request-method :delete :content-type "application/edn" - :body (pr-str {:user/password "password123" :roll "watcher"})} + :body (pr-str {:user/password "password123" :role "watcher"})} {:keys [status body]} (handler request)] (is (== 204 status))) (let [handler (auth/list-resource (:auth system)) From 7e2215fe2ebd537412174598e6ffdf0c1515c894 Mon Sep 17 00:00:00 2001 From: Akihito Terazawa Date: Wed, 12 Jul 2017 10:20:07 +0900 Subject: [PATCH 3/9] Enable oauth2.0 authentication. --- dev/dev.clj | 1 + project.clj | 3 + .../control_bus/component/auth.clj | 137 ++++++++++++++++-- src/clj/job_streamer/control_bus/config.clj | 7 + .../job_streamer/control_bus/endpoint/api.clj | 6 + src/clj/job_streamer/control_bus/main.clj | 1 + src/clj/job_streamer/control_bus/system.clj | 2 +- src/clj/job_streamer/control_bus/util.clj | 14 +- .../control_bus/component/apps_test.clj | 1 + .../control_bus/component/auth_test.clj | 1 + .../control_bus/component/calendar_test.clj | 1 + .../control_bus/component/jobs_test.clj | 1 + .../control_bus/component/scheduler_test.clj | 1 + 13 files changed, 160 insertions(+), 16 deletions(-) diff --git a/dev/dev.clj b/dev/dev.clj index e99d0a4..ea99126 100644 --- a/dev/dev.clj +++ b/dev/dev.clj @@ -19,6 +19,7 @@ (def config (meta-merge config/defaults + config/resource-file config/environ dev-config)) diff --git a/project.clj b/project.clj index 67b5810..46f26ba 100644 --- a/project.clj +++ b/project.clj @@ -25,6 +25,9 @@ [ch.qos.logback/logback-classic "1.1.7"] [org.jboss.weld.se/weld-se "2.2.7.Final"] [net.unit8.weld/weld-prescan "0.1.0"] + [crypto-random "1.2.0"] + [clj-http "3.6.1"] + [ring/ring-codec "1.0.1"] ;; for Scheduler [org.quartz-scheduler/quartz "2.2.3"] diff --git a/src/clj/job_streamer/control_bus/component/auth.clj b/src/clj/job_streamer/control_bus/component/auth.clj index 9884a3a..e0950e7 100644 --- a/src/clj/job_streamer/control_bus/component/auth.clj +++ b/src/clj/job_streamer/control_bus/component/auth.clj @@ -8,12 +8,28 @@ [liberator.core :as liberator] [liberator.representation :refer [ring-response]] [clojure.string :as str] + [clojure.data.json :as json] + [clj-http.client :as client] [ring.util.response :refer [response content-type header redirect]] (job-streamer.control-bus [validation :refer [validate]] - [util :refer [parse-body]]) + [util :refer [parse-body format-url generate-token]]) (job-streamer.control-bus.component [datomic :as d] [token :as token]))) +(defn- find-permissions [datomic user-id app-name] + (->> (d/query datomic + '{:find [?permission] + :in [$ ?app-name ?user-id] + :where [[?member :member/user ?user] + [?member :member/rolls ?roll] + [?roll :roll/permissions ?permission] + [?user :user/id ?user-id] + [?app :application/members ?member] + [?app :application/name ?app-name]]} + app-name user-id) + (apply concat) + set)) + (defn- auth-by-password [datomic user-id password app-name] (when (and (not-empty user-id) (not-empty password)) (when-let [user (d/query datomic @@ -30,18 +46,20 @@ [?app :application/members ?member] [?app :application/name ?app-name]]} user-id password app-name)] - (let [permissions (->> (d/query datomic - '{:find [?permission] - :in [$ ?app-name ?user-id] - :where [[?member :member/user ?user] - [?member :member/rolls ?roll] - [?roll :roll/permissions ?permission] - [?user :user/id ?user-id] - [?app :application/members ?member] - [?app :application/name ?app-name]]} - app-name user-id) - (apply concat) - set)] + (let [permissions (find-permissions datomic user-id app-name)] + (assoc user :permissions permissions))))) + +(defn- find-user [datomic user-id app-name] + (when (not-empty user-id) + (when-let [user (d/query datomic + '{:find [(pull ?s [:*]) .] + :in [$ ?uname ?app-name] + :where [[?s :user/id ?uname] + [?member :member/user ?s] + [?app :application/members ?member] + [?app :application/name ?app-name]]} + user-id app-name)] + (let [permissions (find-permissions datomic user-id app-name)] (assoc user :permissions permissions))))) (defvalidator unique-name-validator @@ -97,6 +115,49 @@ (log/infof "Signup %s as %s succeeded." (:user/id user) roll-name) result)))) +(defn fetch-access-token [{:keys [oauth-providers control-bus-url]} provider-id code] + (when (and (not-empty code) (oauth-providers provider-id)) + (log/info "Fetch access-token with code :" code) + (let [{:keys [domain token-endpoint client-id]} (oauth-providers provider-id) + url (str domain "/" token-endpoint) + query {:code code + :grant_type "authorization_code" + :client_id client-id + :redirect_uri (str control-bus-url "/oauth/" provider-id "/cb")} + {body :body status :status} (client/post url {:form-params query :throw-exceptions false})] + (when (= 200 status) + (let [{:keys [access_token]} (json/read-str body :key-fn keyword)] + (when access_token + (log/info "access-token :" access_token) + access_token)))))) + +(defn check-access-token [{:keys [oauth-providers]} provider-id access-token] + (when (and (not-empty access-token) (oauth-providers provider-id)) + (log/info "check access-token :" access-token) + (let [{:keys [domain introspection-endpoint]} (oauth-providers provider-id) + url (str domain "/" introspection-endpoint) + query {:token_type_hint "access_token" + :token access-token} + {:keys [status body] :as res} (client/post url {:form-params query + :content-type :x-www-form-urlencoded + :throw-exceptions false})] + (when (= 200 status) + (let [{:keys [username active sub]} (json/read-str body :key-fn keyword)] + (when active + (log/infof "active access-token : %s, user-id : %s" access-token (or username sub)) + {:user/id (or username sub)})))))) + +(defn oauth-by + [{:keys [datomic] :as auth-component} provider-id access-token] + (let [app-name "default"] + (if-let [identity (some-> (check-access-token auth-component provider-id access-token) + :user/id + (#(find-user datomic % app-name)) + (select-keys [:user/id :permissions]))] + identity + (-> (find-user datomic "guest" app-name) + (select-keys [:user/id :permissions]))))) + (defn auth-resource [{:keys [datomic token] :as component}] (liberator/resource @@ -154,6 +215,53 @@ (d/transact datomic [[:db.fn/retractEntity [:user/id user-id]]])))) +(defn oauth-resource [{:keys [oauth-providers]}] + (liberator/resource + :available-media-types ["application/edn" "application/json"] + :allowed-methods [:get] + :exists? (fn [_] + {:providers (->> oauth-providers + (map (fn [[id provider]] + [id (select-keys provider [:name :icon])])) + (into {}))}) + :handle-ok (fn [{:keys [providers]}] + providers))) + +(defn redirect-to-auth-provider [{:keys [oauth-providers control-bus-url console-url]} provider-id] + (fn [request] + (if-let [{:keys [domain auth-endpoint client-id scope]} (oauth-providers provider-id)] + (let [state (generate-token) + session-with-state (assoc (:session request) :state state) + endpoint {:url (str domain "/" auth-endpoint) + :query {:client_id client-id + :scope scope + :response_type "code" + :redirect_uri (str control-bus-url "/oauth/" provider-id "/cb") + :state state}}] + (log/info "Redirect to auth provider :" provider-id) + (-> endpoint + format-url + redirect + (assoc :session session-with-state))) + (do (log/infof "Redirect attempt to auth provider, %s, failed because of no configuration." provider-id) + (-> (redirect (str console-url "/login")) + (assoc :body (pr-str {:messages [(str "No configuration : " provider-id)]}))))))) + +(defn oauth-callback [{:keys [console-url] :as auth} provider-id] + (fn [request] + (let [{:keys [state code error]} (:params request) + session-state (get-in request [:session :state])] + (if (and (some? code) + (= state session-state)) + (if-let [identity (some->> code + (fetch-access-token auth provider-id) + (oauth-by auth provider-id))] + (do (log/info "Login attempt succeeded :" (:user/id identity)) + (-> (redirect console-url) + (assoc-in [:session :identity] identity))) + (redirect (str console-url "/login"))) + (redirect (str console-url "/login")))))) + (defrecord Auth [datomic] component/Lifecycle @@ -185,7 +293,8 @@ :in $ ?n :where [?e :user/id ?n]] "admin") - (signup datomic {:user/id "admin" :user/password "password123"} "admin")) + (signup datomic {:user/id "admin" :user/password "password123"} "admin") + (signup datomic {:user/id "guest" :user/password "password123"} "operator")) component) diff --git a/src/clj/job_streamer/control_bus/config.clj b/src/clj/job_streamer/control_bus/config.clj index 1742fdf..a323db6 100644 --- a/src/clj/job_streamer/control_bus/config.clj +++ b/src/clj/job_streamer/control_bus/config.clj @@ -1,5 +1,7 @@ (ns job-streamer.control-bus.config (:require [environ.core :refer [env]] + [clojure.java.io :as io] + [clojure.edn :as edn] [job-streamer.control-bus.model :as model])) (def defaults @@ -24,3 +26,8 @@ :auth {:access-control-allow-origin access-control-allow-origin} :datomic {:uri datomic-uri}})) +(def resource-file + (some-> "job-streamer-control-bus/config.edn" + io/resource + slurp + edn/read-string)) diff --git a/src/clj/job_streamer/control_bus/endpoint/api.clj b/src/clj/job_streamer/control_bus/endpoint/api.clj index c1b05e2..3923902 100644 --- a/src/clj/job_streamer/control_bus/endpoint/api.clj +++ b/src/clj/job_streamer/control_bus/endpoint/api.clj @@ -27,6 +27,12 @@ (ANY "/users" [] (auth/list-resource auth)) (ANY "/user" [] (auth/entry-resource auth nil)) (ANY ["/user/:user-id" :user-id #".*"] [user-id] (auth/entry-resource auth user-id)) + (ANY "/oauth" [] + (auth/oauth-resource auth)) + (ANY "/oauth/:provider-id" [provider-id] + (auth/redirect-to-auth-provider auth provider-id)) + (ANY "/oauth/:provider-id/cb" [provider-id] + (auth/oauth-callback auth provider-id)) ;; Job (ANY "/:app-name/jobs" [app-name] diff --git a/src/clj/job_streamer/control_bus/main.clj b/src/clj/job_streamer/control_bus/main.clj index 0c2218f..0fcd32e 100644 --- a/src/clj/job_streamer/control_bus/main.clj +++ b/src/clj/job_streamer/control_bus/main.clj @@ -13,6 +13,7 @@ (def config (meta-merge config/defaults + config/resource-file config/environ prod-config)) diff --git a/src/clj/job_streamer/control_bus/system.clj b/src/clj/job_streamer/control_bus/system.clj index 0000a49..e558dc9 100644 --- a/src/clj/job_streamer/control_bus/system.clj +++ b/src/clj/job_streamer/control_bus/system.clj @@ -57,7 +57,7 @@ (header "Access-Control-Allow-Origin" access-control-allow-origin) (header "Access-Control-Allow-Credentials" "true")))))) -(def access-rules [{:pattern #"^/(?!auth|user|healthcheck|version).*$" +(def access-rules [{:pattern #"^/(?!auth|oauth|user|healthcheck|version).*$" :handler authenticated?}]) (defn token-base [token-provider] diff --git a/src/clj/job_streamer/control_bus/util.clj b/src/clj/job_streamer/control_bus/util.clj index 7156c37..5451e4b 100644 --- a/src/clj/job_streamer/control_bus/util.clj +++ b/src/clj/job_streamer/control_bus/util.clj @@ -4,7 +4,10 @@ [clojure.java.io :as io] [datomic.api :as d] [ring.util.request :refer [content-type]] - [clojure.data.json :as json]) + [ring.util.codec :refer [form-encode]] + [clojure.data.json :as json] + [clojure.string :as string] + [crypto.random :as random]) (:import [org.jsoup Jsoup])) (defn to-int [n default-value] @@ -136,3 +139,12 @@ (catch Exception e (log/error e "fail to parse edn.") {:message (format "IOException: %s" (.getMessage e))})))) + +(defn format-url [{:keys [query url]}] + (->> (form-encode query) + (str url "?"))) + +(defn generate-token + "Generates random string for anti-forgery-token." + [] + (string/replace (random/base64 60) #"[\+=/]" "-")) \ No newline at end of file diff --git a/test/clj/job_streamer/control_bus/component/apps_test.clj b/test/clj/job_streamer/control_bus/component/apps_test.clj index 4858515..9de0440 100644 --- a/test/clj/job_streamer/control_bus/component/apps_test.clj +++ b/test/clj/job_streamer/control_bus/component/apps_test.clj @@ -19,6 +19,7 @@ (def config (meta-merge config/defaults + config/resource-file config/environ test-config)) diff --git a/test/clj/job_streamer/control_bus/component/auth_test.clj b/test/clj/job_streamer/control_bus/component/auth_test.clj index 0a4953e..2a32360 100644 --- a/test/clj/job_streamer/control_bus/component/auth_test.clj +++ b/test/clj/job_streamer/control_bus/component/auth_test.clj @@ -21,6 +21,7 @@ (def config (meta-merge config/defaults + config/resource-file config/environ test-config)) diff --git a/test/clj/job_streamer/control_bus/component/calendar_test.clj b/test/clj/job_streamer/control_bus/component/calendar_test.clj index 34ac775..9a91545 100644 --- a/test/clj/job_streamer/control_bus/component/calendar_test.clj +++ b/test/clj/job_streamer/control_bus/component/calendar_test.clj @@ -22,6 +22,7 @@ (def config (meta-merge config/defaults + config/resource-file config/environ test-config)) diff --git a/test/clj/job_streamer/control_bus/component/jobs_test.clj b/test/clj/job_streamer/control_bus/component/jobs_test.clj index 6e92f05..8032e51 100644 --- a/test/clj/job_streamer/control_bus/component/jobs_test.clj +++ b/test/clj/job_streamer/control_bus/component/jobs_test.clj @@ -23,6 +23,7 @@ (def config (meta-merge config/defaults + config/resource-file config/environ test-config)) diff --git a/test/clj/job_streamer/control_bus/component/scheduler_test.clj b/test/clj/job_streamer/control_bus/component/scheduler_test.clj index 83fab03..f416226 100644 --- a/test/clj/job_streamer/control_bus/component/scheduler_test.clj +++ b/test/clj/job_streamer/control_bus/component/scheduler_test.clj @@ -17,6 +17,7 @@ (def config (meta-merge config/defaults + config/resource-file config/environ test-config)) From 4b2e2c4bf9c4c49f0633d8e0304cb7f6326f4340 Mon Sep 17 00:00:00 2001 From: Akihito Terazawa Date: Wed, 12 Jul 2017 11:59:21 +0900 Subject: [PATCH 4/9] Fix addding new line at end. --- src/clj/job_streamer/control_bus/util.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/job_streamer/control_bus/util.clj b/src/clj/job_streamer/control_bus/util.clj index 5451e4b..9c58e91 100644 --- a/src/clj/job_streamer/control_bus/util.clj +++ b/src/clj/job_streamer/control_bus/util.clj @@ -147,4 +147,4 @@ (defn generate-token "Generates random string for anti-forgery-token." [] - (string/replace (random/base64 60) #"[\+=/]" "-")) \ No newline at end of file + (string/replace (random/base64 60) #"[\+=/]" "-")) From 98b57ab6d33b25ebdf7dd5d3572f7771b7504ce1 Mon Sep 17 00:00:00 2001 From: Akihito Terazawa Date: Wed, 12 Jul 2017 12:05:01 +0900 Subject: [PATCH 5/9] Fix typo. --- src/clj/job_streamer/control_bus/component/auth.clj | 2 +- test/clj/job_streamer/control_bus/component/auth_test.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/job_streamer/control_bus/component/auth.clj b/src/clj/job_streamer/control_bus/component/auth.clj index 9884a3a..be6ea79 100644 --- a/src/clj/job_streamer/control_bus/component/auth.clj +++ b/src/clj/job_streamer/control_bus/component/auth.clj @@ -115,7 +115,7 @@ (ring-response {:session {:identity (select-keys user [:user/id :permissions])} :body (pr-str {:token (str access-token)})})) (do (log/info "Login attempt failed because of authentification failure.") - (ring-response {:status 401 :body (pr-str {:messages ["Autification failure."]})}))))) + (ring-response {:status 401 :body (pr-str {:messages ["Authentication failure."]})}))))) :handle-no-content (fn [_] (ring-response {:session {}})))) (defn list-resource diff --git a/test/clj/job_streamer/control_bus/component/auth_test.clj b/test/clj/job_streamer/control_bus/component/auth_test.clj index 0a4953e..1f02740 100644 --- a/test/clj/job_streamer/control_bus/component/auth_test.clj +++ b/test/clj/job_streamer/control_bus/component/auth_test.clj @@ -70,7 +70,7 @@ {:keys [status body session headers] :as res} (handler request)] (are [x y] (= x y) 401 status - {:messages ["Autification failure."]} (-> body edn/read-string) + {:messages ["Authentication failure."]} (-> body edn/read-string) nil (:identity session)))) (testing "login as created user" (let [request {:request-method :post From 77a4373cac6cf2b26f4358d0ce7f94a96e75b3fa Mon Sep 17 00:00:00 2001 From: Akihito Terazawa Date: Fri, 14 Jul 2017 13:37:53 +0900 Subject: [PATCH 6/9] Enable to use github & yahoo. --- project.clj | 1 - .../control_bus/component/auth.clj | 96 ++++++++----------- src/clj/job_streamer/control_bus/util.clj | 5 - 3 files changed, 42 insertions(+), 60 deletions(-) diff --git a/project.clj b/project.clj index 46f26ba..ac9c4a1 100644 --- a/project.clj +++ b/project.clj @@ -26,7 +26,6 @@ [org.jboss.weld.se/weld-se "2.2.7.Final"] [net.unit8.weld/weld-prescan "0.1.0"] [crypto-random "1.2.0"] - [clj-http "3.6.1"] [ring/ring-codec "1.0.1"] ;; for Scheduler diff --git a/src/clj/job_streamer/control_bus/component/auth.clj b/src/clj/job_streamer/control_bus/component/auth.clj index e0950e7..8f5cb34 100644 --- a/src/clj/job_streamer/control_bus/component/auth.clj +++ b/src/clj/job_streamer/control_bus/component/auth.clj @@ -9,10 +9,12 @@ [liberator.representation :refer [ring-response]] [clojure.string :as str] [clojure.data.json :as json] - [clj-http.client :as client] + [clojure.data.codec.base64 :as base64] + [org.httpkit.client :as http] [ring.util.response :refer [response content-type header redirect]] + [ring.util.codec :refer [form-encode]] (job-streamer.control-bus [validation :refer [validate]] - [util :refer [parse-body format-url generate-token]]) + [util :refer [parse-body generate-token]]) (job-streamer.control-bus.component [datomic :as d] [token :as token]))) @@ -115,49 +117,34 @@ (log/infof "Signup %s as %s succeeded." (:user/id user) roll-name) result)))) -(defn fetch-access-token [{:keys [oauth-providers control-bus-url]} provider-id code] +(defn fetch-access-token [{:keys [oauth-providers control-bus-url]} provider-id state code] (when (and (not-empty code) (oauth-providers provider-id)) (log/info "Fetch access-token with code :" code) - (let [{:keys [domain token-endpoint client-id]} (oauth-providers provider-id) + (let [{:keys [domain token-endpoint client-id client-secret]} (oauth-providers provider-id) url (str domain "/" token-endpoint) - query {:code code - :grant_type "authorization_code" - :client_id client-id - :redirect_uri (str control-bus-url "/oauth/" provider-id "/cb")} - {body :body status :status} (client/post url {:form-params query :throw-exceptions false})] + query (-> (merge {:code code + :state state + :grant_type "authorization_code" + :client_id client-id + :redirect_uri (str control-bus-url "/oauth/" provider-id "/cb")} + (when client-secret {:client_secret client-secret})) + form-encode) + auth-header (->> (str client-id ":" client-secret) + .getBytes + base64/encode + String. + (str "Basic ")) + {:keys [body status] :as res} @(http/post (str url "?" query) + {:body query + :headers {"Accept" "application/json" + "Authorization" auth-header + "Content-Type" "application/x-www-form-urlencoded"}})] (when (= 200 status) (let [{:keys [access_token]} (json/read-str body :key-fn keyword)] (when access_token (log/info "access-token :" access_token) access_token)))))) -(defn check-access-token [{:keys [oauth-providers]} provider-id access-token] - (when (and (not-empty access-token) (oauth-providers provider-id)) - (log/info "check access-token :" access-token) - (let [{:keys [domain introspection-endpoint]} (oauth-providers provider-id) - url (str domain "/" introspection-endpoint) - query {:token_type_hint "access_token" - :token access-token} - {:keys [status body] :as res} (client/post url {:form-params query - :content-type :x-www-form-urlencoded - :throw-exceptions false})] - (when (= 200 status) - (let [{:keys [username active sub]} (json/read-str body :key-fn keyword)] - (when active - (log/infof "active access-token : %s, user-id : %s" access-token (or username sub)) - {:user/id (or username sub)})))))) - -(defn oauth-by - [{:keys [datomic] :as auth-component} provider-id access-token] - (let [app-name "default"] - (if-let [identity (some-> (check-access-token auth-component provider-id access-token) - :user/id - (#(find-user datomic % app-name)) - (select-keys [:user/id :permissions]))] - identity - (-> (find-user datomic "guest" app-name) - (select-keys [:user/id :permissions]))))) - (defn auth-resource [{:keys [datomic token] :as component}] (liberator/resource @@ -222,43 +209,44 @@ :exists? (fn [_] {:providers (->> oauth-providers (map (fn [[id provider]] - [id (select-keys provider [:name :icon])])) + [id (select-keys provider [:name :class-name])])) (into {}))}) :handle-ok (fn [{:keys [providers]}] providers))) (defn redirect-to-auth-provider [{:keys [oauth-providers control-bus-url console-url]} provider-id] (fn [request] - (if-let [{:keys [domain auth-endpoint client-id scope]} (oauth-providers provider-id)] + (if-let [{:keys [domain auth-endpoint client-id client-secret scope]} (oauth-providers provider-id)] (let [state (generate-token) session-with-state (assoc (:session request) :state state) - endpoint {:url (str domain "/" auth-endpoint) - :query {:client_id client-id - :scope scope - :response_type "code" - :redirect_uri (str control-bus-url "/oauth/" provider-id "/cb") - :state state}}] + url (str domain "/" auth-endpoint) + query (merge {:client_id client-id + :response_type "code" + :redirect_uri (str control-bus-url "/oauth/" provider-id "/cb") + :state state} + (when client-secret {:client_secret client-secret}) + (when scope {:scope scope}))] (log/info "Redirect to auth provider :" provider-id) - (-> endpoint - format-url + (-> (str url "?" (form-encode query)) redirect (assoc :session session-with-state))) (do (log/infof "Redirect attempt to auth provider, %s, failed because of no configuration." provider-id) (-> (redirect (str console-url "/login")) (assoc :body (pr-str {:messages [(str "No configuration : " provider-id)]}))))))) -(defn oauth-callback [{:keys [console-url] :as auth} provider-id] +(defn oauth-callback [{:keys [datomic console-url] :as auth} provider-id] (fn [request] - (let [{:keys [state code error]} (:params request) + (let [app-name "default" + {:keys [state code error]} (:params request) session-state (get-in request [:session :state])] (if (and (some? code) (= state session-state)) - (if-let [identity (some->> code - (fetch-access-token auth provider-id) - (oauth-by auth provider-id))] - (do (log/info "Login attempt succeeded :" (:user/id identity)) - (-> (redirect console-url) - (assoc-in [:session :identity] identity))) + (if-let [token (fetch-access-token auth provider-id state code)] + (let [identity (-> (find-user datomic "guest" app-name) + (select-keys [:user/id :permissions]))] + (log/info "Login attempt succeeded :" (:user/id identity)) + (-> (redirect console-url) + (assoc-in [:session :identity] identity))) (redirect (str console-url "/login"))) (redirect (str console-url "/login")))))) diff --git a/src/clj/job_streamer/control_bus/util.clj b/src/clj/job_streamer/control_bus/util.clj index 9c58e91..44dd526 100644 --- a/src/clj/job_streamer/control_bus/util.clj +++ b/src/clj/job_streamer/control_bus/util.clj @@ -4,7 +4,6 @@ [clojure.java.io :as io] [datomic.api :as d] [ring.util.request :refer [content-type]] - [ring.util.codec :refer [form-encode]] [clojure.data.json :as json] [clojure.string :as string] [crypto.random :as random]) @@ -140,10 +139,6 @@ (log/error e "fail to parse edn.") {:message (format "IOException: %s" (.getMessage e))})))) -(defn format-url [{:keys [query url]}] - (->> (form-encode query) - (str url "?"))) - (defn generate-token "Generates random string for anti-forgery-token." [] From 97b24815feebead839f76202bb616891c5d25eb4 Mon Sep 17 00:00:00 2001 From: Akihito Terazawa Date: Fri, 14 Jul 2017 14:19:11 +0900 Subject: [PATCH 7/9] Add dependency of base64. --- project.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/project.clj b/project.clj index ac9c4a1..b537ae9 100644 --- a/project.clj +++ b/project.clj @@ -27,6 +27,7 @@ [net.unit8.weld/weld-prescan "0.1.0"] [crypto-random "1.2.0"] [ring/ring-codec "1.0.1"] + [org.clojure/data.codec "0.1.0"] ;; for Scheduler [org.quartz-scheduler/quartz "2.2.3"] From c29c5e42e215c70f1bd1e5396ca39fbf90203903 Mon Sep 17 00:00:00 2001 From: Akihito Terazawa Date: Fri, 14 Jul 2017 14:42:02 +0900 Subject: [PATCH 8/9] Fix auth_test. --- src/clj/job_streamer/control_bus/component/auth.clj | 4 ++-- test/clj/job_streamer/control_bus/component/auth_test.clj | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/clj/job_streamer/control_bus/component/auth.clj b/src/clj/job_streamer/control_bus/component/auth.clj index 724be2a..22cd4eb 100644 --- a/src/clj/job_streamer/control_bus/component/auth.clj +++ b/src/clj/job_streamer/control_bus/component/auth.clj @@ -23,8 +23,8 @@ '{:find [?permission] :in [$ ?app-name ?user-id] :where [[?member :member/user ?user] - [?member :member/rolls ?roll] - [?roll :roll/permissions ?permission] + [?member :member/roles ?role] + [?role :role/permissions ?permission] [?user :user/id ?user-id] [?app :application/members ?member] [?app :application/name ?app-name]]} diff --git a/test/clj/job_streamer/control_bus/component/auth_test.clj b/test/clj/job_streamer/control_bus/component/auth_test.clj index e265c65..4fe46a6 100644 --- a/test/clj/job_streamer/control_bus/component/auth_test.clj +++ b/test/clj/job_streamer/control_bus/component/auth_test.clj @@ -106,7 +106,7 @@ {:keys [status body]} (handler request)] (are [x y] (= x y) 200 status - 1 (-> body edn/read-string count) + 2 (-> body edn/read-string count) "admin" (-> body edn/read-string first :user/id)))))) (deftest entry-resource @@ -188,7 +188,7 @@ {:keys [status body]} (handler request)] (are [x y] (= x y) 200 status - 2 (-> body edn/read-string count)))) + 3 (-> body edn/read-string count)))) (let [system (new-system config) handler (auth/entry-resource (:auth system) "test")] (testing "delete user" @@ -207,4 +207,4 @@ {:keys [status body]} (handler request)] (are [x y] (= x y) 200 status - 1 (-> body edn/read-string count))))))) + 2 (-> body edn/read-string count))))))) From 821e34c4d34f12f8cc40134e44f973506a638c75 Mon Sep 17 00:00:00 2001 From: seki Date: Fri, 14 Jul 2017 18:28:24 +0900 Subject: [PATCH 9/9] Fixed the virsion for release. --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 5902d52..29fa924 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.0-SNAPSHOT +1.1.0-BETA