From 4001a8b88f2926fb893c819ffc83d374efe1db7f Mon Sep 17 00:00:00 2001 From: Milt Reder Date: Thu, 17 Feb 2022 16:46:48 -0500 Subject: [PATCH 1/4] SQL-135 simple test rig for LRS auth --- keycloak/docker-compose.yml | 2 + resources/public/oidc.json | 2 +- src/dev/com/yetanalytics/re_oidc/demo.cljs | 79 +++++++++++++++++++++- 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/keycloak/docker-compose.yml b/keycloak/docker-compose.yml index e39f875..7d5b3c9 100644 --- a/keycloak/docker-compose.yml +++ b/keycloak/docker-compose.yml @@ -9,6 +9,8 @@ services: image: quay.io/keycloak/keycloak:16.1.0 environment: KEYCLOAK_IMPORT: /tmp/test-realm.json + KEYCLOAK_USER: admin + KEYCLOAK_PASSWORD: changeme123 configs: - source: test_realm target: /tmp/test-realm.json diff --git a/resources/public/oidc.json b/resources/public/oidc.json index c61c136..dad15d9 100644 --- a/resources/public/oidc.json +++ b/resources/public/oidc.json @@ -4,7 +4,7 @@ "redirect_uri": "http://localhost:9500/login-callback", "response_type": "code", "post_logout_redirect_uri": "http://localhost:9500/logout-callback", - "scope": "openid profile", + "scope": "openid profile all", "automaticSilentRenew": true, "monitorSession": false, "filterProtocolClaims": false diff --git a/src/dev/com/yetanalytics/re_oidc/demo.cljs b/src/dev/com/yetanalytics/re_oidc/demo.cljs index 8e977a4..a752400 100644 --- a/src/dev/com/yetanalytics/re_oidc/demo.cljs +++ b/src/dev/com/yetanalytics/re_oidc/demo.cljs @@ -25,7 +25,9 @@ (def static-config {:auto-login false :on-login-success #(push-state "/") - :on-logout-success #(push-state "/")}) + :on-logout-success #(push-state "/") + :on-get-user-success #(println "get user success" %) + :user-store :local-storage}) ;; Init the demo's DB (re-frame/reg-event-db @@ -52,7 +54,7 @@ ;; Initialize OIDC from the remote config [::re-oidc/init (assoc static-config - :user-store :local-storage + ;; These config options are passed directly to the OIDC client :oidc-config config)]]]})) @@ -93,6 +95,72 @@ (.error js/console "Failed to get token echo from api server, status:" status) {})) +;; XAPI stuff +(re-frame/reg-event-fx + ::get-statements! + (fn [{{status ::re-oidc/status + :as db} :db} _] + (if (= :loaded status) + (let [{{:keys [access-token]} ::re-oidc/user} db] + {:http-xhrio {:uri "http://0.0.0.0:8081/xapi/statements" + :method :get + :headers {"X-Experience-Api-Version" "1.0.3" + "Authorization" (format "Bearer %s" access-token)} + :response-format (ajax/json-response-format + {:keywords? true}) + :on-success [::recv-get-statements] + :on-failure [::fail-get-statements]}}) + (do + (.error js/console "Can't call XAPI if not logged in!") + {})))) + +(re-frame/reg-event-db + ::recv-get-statements + (fn [db [_ statement-response]] + (assoc db ::statement-response statement-response))) + +(re-frame/reg-event-fx + ::fail-get-statements + (fn [ctx [_ {:keys [status]}]] + (.error js/console "Failed to get statements, status:" status) + {})) + +(re-frame/reg-event-fx + ::post-statement! + (fn [{{status ::re-oidc/status + :as db} :db} _] + (if (= :loaded status) + (let [{{:keys [access-token]} ::re-oidc/user} db] + {:http-xhrio {:uri "http://0.0.0.0:8081/xapi/statements" + :method :post + :headers {"Content-Type" "application/json" + "X-Experience-Api-Version" "1.0.3" + "Authorization" (format "Bearer %s" access-token)} + :format (ajax/json-request-format) + :response-format (ajax/json-response-format + {:keywords? true}) + :on-success [::recv-post-statement] + :on-failure [::fail-post-statement] + :params + {"actor" {"mbox" "mailto:bob@example.com"} + "verb" {"id" "https://example.com/verbs/foo"} + "object" {"id" "https://example.com/activity"}}}}) + (do + (.error js/console "Can't call XAPI if not logged in!") + {})))) + +(re-frame/reg-event-db + ::recv-post-statement + (fn [db [_ statement-post-response]] + (assoc db ::statement-post-response statement-post-response))) + +(re-frame/reg-event-fx + ::fail-post-statement + (fn [ctx [_ {:keys [status]}]] + (.error js/console "Failed to post statement, status:" status) + {})) + + ;; Compose init events for the demo db & getting remote config (re-frame/reg-event-fx ::init! @@ -141,6 +209,13 @@ [:button "Loading..."]] :loaded [:div + [:div + [:button {:on-click #(re-frame/dispatch [::get-statements!])} + "Get Statements"] + [:button {:on-click #(re-frame/dispatch [::post-statement!])} + "Post Statement"] + ] + [:button {:on-click #(re-frame/dispatch [::echo-token!])} "Echo Token"] From 4e9ce6a7085f863b983d63a993d29238ce572a48 Mon Sep 17 00:00:00 2001 From: Milt Reder Date: Tue, 8 Mar 2022 11:03:59 -0500 Subject: [PATCH 2/4] SQL-140 works with lrsql keycloak demo --- resources/public/oidc.json | 10 +++++----- src/dev/com/yetanalytics/re_oidc/demo.cljs | 18 +++++++----------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/resources/public/oidc.json b/resources/public/oidc.json index dad15d9..ece1950 100644 --- a/resources/public/oidc.json +++ b/resources/public/oidc.json @@ -1,10 +1,10 @@ { - "authority": "http://0.0.0.0:8080/auth/realms/test", - "client_id": "testapp_public", - "redirect_uri": "http://localhost:9500/login-callback", + "authority": "http://0.0.0.0:8081/auth/realms/test", + "client_id": "lrs_admin_ui", + "redirect_uri": "http://localhost:9500", "response_type": "code", - "post_logout_redirect_uri": "http://localhost:9500/logout-callback", - "scope": "openid profile all", + "post_logout_redirect_uri": "http://localhost:9500", + "scope": "openid profile lrs:all lrs:admin", "automaticSilentRenew": true, "monitorSession": false, "filterProtocolClaims": false diff --git a/src/dev/com/yetanalytics/re_oidc/demo.cljs b/src/dev/com/yetanalytics/re_oidc/demo.cljs index a752400..97c6c88 100644 --- a/src/dev/com/yetanalytics/re_oidc/demo.cljs +++ b/src/dev/com/yetanalytics/re_oidc/demo.cljs @@ -102,7 +102,7 @@ :as db} :db} _] (if (= :loaded status) (let [{{:keys [access-token]} ::re-oidc/user} db] - {:http-xhrio {:uri "http://0.0.0.0:8081/xapi/statements" + {:http-xhrio {:uri "http://0.0.0.0:8080/xapi/statements" :method :get :headers {"X-Experience-Api-Version" "1.0.3" "Authorization" (format "Bearer %s" access-token)} @@ -131,7 +131,7 @@ :as db} :db} _] (if (= :loaded status) (let [{{:keys [access-token]} ::re-oidc/user} db] - {:http-xhrio {:uri "http://0.0.0.0:8081/xapi/statements" + {:http-xhrio {:uri "http://0.0.0.0:8080/xapi/statements" :method :post :headers {"Content-Type" "application/json" "X-Experience-Api-Version" "1.0.3" @@ -183,15 +183,11 @@ (defn process-callbacks! "Detect post login/logout callbacks and issue route dispatch to re-oidc." [& _] - (case js/window.location.pathname - "/login-callback" (re-frame/dispatch - [::re-oidc/login-callback - static-config - js/window.location.search]) - "/logout-callback" (re-frame/dispatch - [::re-oidc/logout-callback - static-config]) - nil)) + (when-let [search (not-empty js/window.location.search)] + (re-frame/dispatch + [::re-oidc/login-callback + static-config + search]))) (defn hello-world [] [:div From 71f14a3a8057ebfa281bc0dccfd78b3b98817a7c Mon Sep 17 00:00:00 2001 From: Milt Reder Date: Tue, 8 Mar 2022 15:14:19 -0500 Subject: [PATCH 3/4] accept callbacks for oidc lifecycle load/unload --- src/lib/com/yetanalytics/re_oidc.cljs | 32 +++++++++++++++++++-------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/lib/com/yetanalytics/re_oidc.cljs b/src/lib/com/yetanalytics/re_oidc.cljs index 5aa4105..81a748b 100644 --- a/src/lib/com/yetanalytics/re_oidc.cljs +++ b/src/lib/com/yetanalytics/re_oidc.cljs @@ -40,12 +40,18 @@ (defn reg-events! "Register event callbacks to re-frame on the OIDC UserManager" - [^UserManager user-manager] + [^UserManager user-manager + {:keys [on-user-loaded + on-user-unloaded]}] (doto user-manager.events (.addUserLoaded - (u/dispatch-cb [::user-loaded])) + (cond-> (u/dispatch-cb [::user-loaded]) + on-user-loaded + (juxt on-user-loaded))) (.addUserUnloaded - (u/dispatch-cb [::user-unloaded])) + (cond-> (u/dispatch-cb [::user-unloaded]) + on-user-unloaded + (juxt on-user-unloaded))) ;; We set automaticSilentRenew to true and these are done for us #_(.addAccessTokenExpiring (u/dispatch-cb [::access-token-expiring])) @@ -64,12 +70,12 @@ (u/dispatch-cb [::user-session-changed])))) (defn init! - "Initialize the OIDC UserManager from config. Idempotent" - [user-manager config] + "Initialize the OIDC UserManager from config + calllbacks. Idempotent" + [user-manager config lifecycle-callbacks] (if user-manager user-manager (doto (UserManager. (clj->js config)) - reg-events!))) + (reg-events! lifecycle-callbacks)))) (re-frame/reg-fx ::init-fx @@ -77,7 +83,8 @@ state-store user-store] :or {state-store :local-storage - user-store :session-storage}}] + user-store :session-storage} + :as init-input}] (swap! user-manager init! (assoc @@ -99,7 +106,10 @@ :session-storage js/window.sessionStorage ;; custom - user-store)}))))) + user-store)})) + (select-keys init-input + [:on-user-loaded + :on-user-unloaded])))) (defn- throw-not-initialized! [] @@ -329,6 +339,8 @@ on-logout-failure on-get-user-success on-get-user-failure + on-user-loaded + on-user-unloaded state-store user-store redirect-uri-absolution] @@ -347,7 +359,9 @@ redirect-uri-absolution u/absolve-redirect-uris) :state-store state-store - :user-store user-store}] + :user-store user-store + :on-user-loaded on-user-loaded + :on-user-unloaded on-user-unloaded}] (case ?callback :login [::signin-redirect-callback-fx {:query-string ?qstring From 020bf1f3e385267a536b7618d556e371eebe397c Mon Sep 17 00:00:00 2001 From: Milt Reder Date: Tue, 8 Mar 2022 15:25:29 -0500 Subject: [PATCH 4/4] fix tests for extra callbacks --- src/test/com/yetanalytics/re_oidc_test.cljs | 28 +++++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/test/com/yetanalytics/re_oidc_test.cljs b/src/test/com/yetanalytics/re_oidc_test.cljs index d57fb70..f2c9211 100644 --- a/src/test/com/yetanalytics/re_oidc_test.cljs +++ b/src/test/com/yetanalytics/re_oidc_test.cljs @@ -133,7 +133,9 @@ :fx [[::re-oidc/init-fx {:config {} :state-store :local-storage - :user-store :session-storage}] + :user-store :session-storage + :on-user-loaded [::loaded] + :on-user-unloaded [::unloaded]}] [::re-oidc/signin-redirect-callback-fx {:query-string "?foo=bar", :on-success [::success], @@ -144,13 +146,17 @@ [nil {:oidc-config {} :on-login-success [::success] - :on-login-failure [::failure]}])))) + :on-login-failure [::failure] + :on-user-loaded [::loaded] + :on-user-unloaded [::unloaded]}])))) (testing "with logout callback" (is (= {:db {::re-oidc/status :init}, :fx [[::re-oidc/init-fx {:config {} :state-store :local-storage - :user-store :session-storage}] + :user-store :session-storage + :on-user-loaded [::loaded] + :on-user-unloaded [::unloaded]}] [::re-oidc/signout-redirect-callback-fx {:on-success [::success], :on-failure [::failure]}]]} @@ -159,20 +165,26 @@ [nil {:oidc-config {} :on-logout-success [::success] - :on-logout-failure [::failure]}])))) + :on-logout-failure [::failure] + :on-user-loaded [::loaded] + :on-user-unloaded [::unloaded]}])))) (testing "with no callback" (is (= {:db {::re-oidc/status :init}, :fx [[::re-oidc/init-fx {:config {} :state-store :local-storage - :user-store :session-storage}] + :user-store :session-storage + :on-user-loaded [::loaded] + :on-user-unloaded [::unloaded]}] [::re-oidc/get-user-fx - {:auto-login false, - :on-success [::success], + {:auto-login false + :on-success [::success] :on-failure [::failure]}]]} (init {:db {}} [nil {:oidc-config {} :on-get-user-success [::success] - :on-get-user-failure [::failure]}]))))) + :on-get-user-failure [::failure] + :on-user-loaded [::loaded] + :on-user-unloaded [::unloaded]}])))))