~willvaughn/grsroot-auth

695bfe02e3f9b2c5da83444dfc601d34bbcc3454 — William Vaughn 2 months ago 56ade3c
switch to kebab-case everywhere, auto-convert out of hugsql
M project.clj => project.clj +4 -3
@@ 7,6 7,7 @@
                 [buddy/buddy-core "1.10.413"]
                 [buddy/buddy-hashers "1.8.158"]
                 [buddy/buddy-sign "3.4.333"]
                 [camel-snake-kebab "0.4.3"]
                 [ch.qos.logback/logback-classic "1.2.10"]
                 [clojure.java-time "0.3.3"]
                 [conman "0.9.3"]


@@ 38,14 39,14 @@
                 [selmer "1.12.50"]]

  :min-lein-version "2.0.0"
  

  :source-paths ["src/clj"]
  :test-paths ["test/clj"]
  :resource-paths ["resources"]
  :target-path "target/%s/"
  :main ^:skip-aot grsroot-auth.core

  :plugins [] 
  :plugins []

  :profiles
  {:uberjar {:omit-source true


@@ 67,7 68,7 @@
                                 [jonase/eastwood "0.3.5"]
                                 [cider/cider-nrepl "0.28.3"]
                                 [mx.cider/enrich-classpath "1.4.1"]]
                  

                  :source-paths ["env/dev/clj" ]
                  :resource-paths ["env/dev/resources"]
                  :repl-options {:init-ns user

M resources/sql/queries.sql => resources/sql/queries.sql +10 -10
@@ 24,7 24,7 @@ select app_role_id,
  from app_user_role
  join app_role using(app_role_id)
  join app using(app_id)
 where app_user_id = :app_user_id;
 where app_user_id = :app-user-id;

-- :name get-apps :? :*
-- :doc Returns a vector of apps


@@ 38,7 38,7 @@ order by app_slug;
insert into app (
  app_slug
) values (
  :app_slug
  :app-slug
) returning app_id;

-- :name insert-app-role!* :<! :1


@@ 47,11 47,11 @@ insert into app_role (
  app_id,
  role_slug
) values (
  :app_id,
  :role_slug
  :app-id,
  :role-slug
) returning app_role_id;

-- :name list-app-roles
-- :name list-app-roles :? :*
  select a.app_id,
         ar.app_role_id,
         a.app_slug,


@@ 60,7 60,7 @@ insert into app_role (
    join app a using(app_id)
order by a.app_slug, ar.role_slug;

-- :name get-app-roles-by-app-id
-- :name get-app-roles-by-app-id :? :*
-- :doc Get list of roles associated with an app.
select a.app_id,
       ar.app_role_id,


@@ 68,16 68,16 @@ select a.app_id,
       ar.role_slug
  from app_role ar
  join app a using(app_id)
 where app_id = :app_id
 where app_id = :app-id
order by a.app_slug, ar.role_slug;

-- :name insert-app-user-role!* :<!
-- :name insert-app-user-role!* :<! :1
insert into app_user_role (
  app_user_id,
  app_role_id
) values (
  :app_user_id,
  :app_role_id
  :app-user-id,
  :app-role-id
) returning app_user_role_id;

-- -- :name create-user! :! :n

M src/clj/grsroot_auth/db/core.clj => src/clj/grsroot_auth/db/core.clj +25 -1
@@ 7,9 7,33 @@
    [clojure.tools.logging :as log]
    [conman.core :as conman]
    [grsroot-auth.config :refer [env]]
    [mount.core :refer [defstate]])
    [mount.core :refer [defstate]]
    [camel-snake-kebab.extras :refer [transform-keys]]
    [camel-snake-kebab.core :refer [->kebab-case-keyword]])
  (:import (org.postgresql.util PGobject)))

(defn result-one-snake->kebab
  [this result options]
  (->> (hugsql.adapter/result-one this result options)
       (transform-keys ->kebab-case-keyword)))

(defn result-many-snake->kebab
  [this result options]
  (->> (hugsql.adapter/result-many this result options)
       (map #(transform-keys ->kebab-case-keyword %))))

(defmethod hugsql.core/hugsql-result-fn :1 [sym]
  'grsroot-auth.db.core/result-one-snake->kebab)

(defmethod hugsql.core/hugsql-result-fn :one [sym]
  'grsroot-auth.db.core/result-one-snake->kebab)

(defmethod hugsql.core/hugsql-result-fn :* [sym]
  'grsroot-auth.db.core/result-many-snake->kebab)

(defmethod hugsql.core/hugsql-result-fn :many [sym]
  'grsroot-auth.db.core/result-many-snake->kebab)

(defstate ^:dynamic *db*
  :start (if-let [jdbc-url (env :database-url)]
           (conman/connect! {:jdbc-url jdbc-url})

M src/clj/grsroot_auth/middleware.clj => src/clj/grsroot_auth/middleware.clj +2 -2
@@ 17,7 17,7 @@
;; maybe just use a memoized function to load this map?
;; This would be a query of roles for grsroot-auth, and then turning their slug into keywords and their values into strings. Not sure if it's best to use string or be coercing to uuids.
;; This should be able to be a uuid object, something getting lost through json serialization and when token is loaded.
(def my-roles {:super-admin "926e9535-2505-4c9e-a6cf-37953324d90d"})
(def my-roles {:super-admin "aa20eb1b-1758-4b72-a92e-b5f2c586e5e7"})

(defn- pkey [auth-cfg]
  (ks/public-key


@@ 25,7 25,7 @@

(defn any-granted? [user roles]
  (seq
   (s/intersection (set (map :app_role_id (:app_roles user)))
   (s/intersection (set (map :app-role-id (:app-roles user)))
                   (set (vals (select-keys my-roles roles))))))

(defn restrict-by-roles [roles]

M src/clj/grsroot_auth/routes/services.clj => src/clj/grsroot_auth/routes/services.clj +34 -32
@@ 77,7 77,7 @@
   ;; do, and then they put the users here.
   ;; should be POST /api/users
   ;;
   ;; this service is just a database of users and apps, it makes tokens. that's it, the apps themselves have to do the
   ;; this service is just a database of users and apps, it makes tokens. that's it, the apps themselves have to do the work of registering and verifying users.
   ;;
   ;; This API should be open. Should there even be authentication on this? It should be a locked down inaccessible backend service that only my apps can call.
   ;; TODO GET /api/users


@@ 100,23 100,23 @@
    ["" {:get {:id ::list-app-users
               :summary "List app users"
               :responses {200 {:body
                                {:app_users [{:app_user_id uuid?
                                {:app-users [{:app-user_id uuid?
                                              :email string?}]}}}
               :handler (fn [_]
                          (ok {:app_users []}))}
                          (ok {:app-users []}))}
         :post {:id ::create-app-user
                :summary "Create/register a new app user"
                :responses {201 {:body {:app_user_id uuid?
                :responses {201 {:body {:app-user-id uuid?
                                        :email string?}}}
                :handler (fn [_]
                           (created "/api/app-users/1" {:app_user_id (java.util.UUID/randomUUID)
                           (created "/api/app-users/1" {:app-user-id (java.util.UUID/randomUUID)
                                                        :email "vaughnwilld@gmail.com"}))}
         :put {:id ::put-app-user
               :summary "Update app user"
               :responses {200 {:body {:app_user_id uuid?
               :responses {200 {:body {:app-user-id uuid?
                                       :email string?}}}
               :handler (fn [_]
                          (ok {:app_user_id (java.util.UUID/randomUUID)
                          (ok {:app-user-id (java.util.UUID/randomUUID)
                               :email "vaughnwilld@gmail.com"}))}}]]

   ["/apps"


@@ 124,24 124,26 @@
    ["" {:get {:id ::list-apps
               :summary "list auth service apps"
               :responses {200 {:body
                                {:apps [{:app_id uuid?
                                         :app_slug string?
                                         :app_roles [{:app_role_id uuid?
                                                      :role_slug string?}]}]}}}
                                {:apps [{:app-id uuid?
                                         :app-slug string?
                                         :app-roles [{:app-role-id uuid?
                                                      :role-slug string?}]}]}}}
               :handler (fn [_]
                          (ok {:apps (svc/list-apps)}))}
         :post {:id ::create-app
                :swagger {:security [{:apiAuth []}]}
                :summary "create a new app for the auth service"
                :parameters {:body {:app_slug string?}}
                :responses {201 {:body {:app_id uuid? :app_slug string?}}
                :parameters {:body {:app-slug string?}}
                :responses {201 {:body {:app-id uuid? :app-slug string?}}
                            401 {:body {:message string?}}
                            403 {:body {:message string?}}
                            409 {:body {:message string?}}}
                :handler (fn [{{{app-slug :app_slug} :body} :parameters }]
                :handler (fn [{{{:keys [app-slug]} :body} :parameters :as request}]
                           (prn (keys request))
                           (prn (:identity request))
                           (try
                             (let [{:keys [app_id]} (svc/create-app! app-slug)]
                               (created (format "/api/apps/%s" app_id) {:app_id app_id  :app_slug app-slug}))
                             (let [{:keys [app-id]} (svc/create-app! app-slug)]
                               (created (format "/api/apps/%s" app-id) {:app-id app-id  :app-slug app-slug}))
                             (catch clojure.lang.ExceptionInfo e
                               (if (= (:grsroot-auth/error-id (ex-data e))
                                      ::svc/unique-violation)


@@ 151,24 153,24 @@
     ["" {:get {:id ::get-app
                :summary "get single auth service app"
                :parameters {:path {:app-id uuid?}}
                :responses {200 {:body {:app_id uuid?
                                        :app_slug string?
                                        :app_roles [{:app_role_id uuid?
                                                     :role_slug string?}]}}
                :responses {200 {:body {:app-id uuid?
                                        :app-slug string?
                                        :app-roles [{:app-role-id uuid?
                                                     :role-slug string?}]}}
                            404 {:body {:message string?}}}
                :handler (fn [{{{app-id :app-id} :path} :parameters}]
                :handler (fn [{{{:keys [app-id]} :path} :parameters}]
                           (if-let [app (svc/get-app-by-app-id app-id)]
                             (ok app)
                             (not-found {:message "App not found!"})))}}]
     ["/roles" {:get {:id ::get-app-roles
                      :summary "get roles provided by an app"
                      :parameters {:path {:app-id uuid?}}
                      :responses {200 {:body {:app_roles [{:app_role_id uuid?
                                                           :role_slug string?}]
                                              :app {:app_id uuid?
                                                    :app_slug string?}}}
                      :responses {200 {:body {:app-roles [{:app-role-id uuid?
                                                           :role-slug string?}]
                                              :app {:app-id uuid?
                                                    :app-slug string?}}}
                                  404 {:body {:message string?}}}
                      :handler (fn [{{{app-id :app-id} :path} :parameters}]
                      :handler (fn [{{{:keys [app-id]} :path} :parameters}]
                                 (if-let [res (svc/get-app-roles-by-app-id app-id)]
                                   (ok res)
                                   (not-found {:message "App not found!"})))}


@@ 176,16 178,16 @@
                       :swagger {:security [{:apiAuth []}]}
                       :summary "create a new app role"
                       :parameters {:path {:app-id uuid?}
                                    :body {:role_slug string?}}
                       :responses {201 {:body {:app_role_id uuid? :role_slug string?}}
                                    :body {:role-slug string?}}
                       :responses {201 {:body {:app-role-id uuid? :role-slug string?}}
                                   401 {:body {:message string?}}
                                   403 {:body {:message string?}}
                                   409 {:body {:message string?}}}
                       :handler (fn [{{{role-slug :role_slug} :body
                                       {app-id :app-id} :path} :parameters}]
                       :handler (fn [{{{:keys [role-slug]} :body
                                       {:keys [app-id]} :path} :parameters}]
                                  (try
                                    (let [{:keys [app_role_id]} (svc/create-app-role! app-id role-slug)]
                                      (created (format "/api/apps/%s/roles/%s" app-id app_role_id) {:app_role_id app_role_id  :role_slug role-slug}))
                                    (let [{:keys [app-role-id]} (svc/create-app-role! app-id role-slug)]
                                      (created (format "/api/apps/%s/roles/%s" app-id app-role-id) {:app-role-id app-role-id :role-slug role-slug}))
                                    (catch clojure.lang.ExceptionInfo e
                                      (if (= (:grsroot-auth/error-id (ex-data e))
                                             ::svc/unique-violation)

M src/clj/grsroot_auth/service.clj => src/clj/grsroot_auth/service.clj +10 -11
@@ 17,7 17,7 @@
  (jdbc/with-transaction [t-conn db/*db*]
    (if-let [app-user (db/get-app-user-for-auth* t-conn {:email email})]
      (assoc app-user
             :app_roles (db/get-app-user-roles t-conn {:app_user_id (:app_user_id app-user)})))))
             :app-roles (db/get-app-user-roles t-conn app-user)))))

(defn auth-user [creds]
  "Check email and password credentials against stored users.


@@ 64,38 64,37 @@ Returns a pair [ok? res] where res contains a :user field when authenticated and

(defn app-role-rows->app-roles [rows]
  (into []
        (map #(select-keys % [:app_role_id :role_slug]) rows)))
        (map #(select-keys % [:app-role-id :role-slug]) rows)))

(defn app-role-rows->app [rows]
  (let [row (first rows)]
    {:app_id (:app_id row)
     :app_slug (:app_slug row)
     :app_roles (app-role-rows->app-roles rows)}))
    (-> (assoc row :app-roles (app-role-rows->app-roles rows))
        (select-keys [:app-id :app-slug :app-roles]))))

(defn list-apps []
  (let [rows (db/list-app-roles)]
    (into []
          (->> rows
               (partition-by :app_id)
               (partition-by :app-id)
               (map app-role-rows->app)))))

(defn get-app-by-app-id [app-id]
  (if-let [rows (db/get-app-roles-by-app-id {:app_id app-id})]
  (if-let [rows (db/get-app-roles-by-app-id {:app-id app-id})]
    (app-role-rows->app rows)))

(defn create-app! [app-slug]
  (try
    (db/insert-app!* {:app_slug app-slug})
    (db/insert-app!* {:app-slug app-slug})
    (catch clojure.lang.ExceptionInfo e
      (throw-user-sql-error (.getCause e) {::unique-violation "App already exists!"}))))

(defn create-app-role! [app-id role-slug]
  (try
    (db/insert-app-role!* {:app_id app-id :role_slug role-slug})
    (db/insert-app-role!* {:app-id app-id :role-slug role-slug})
    (catch clojure.lang.ExceptionInfo e
      (throw-user-sql-error (.getCause e) {::unique-violation "App role already exists!"}))))

(defn get-app-roles-by-app-id [app-id]
  (if-let [app (get-app-by-app-id app-id)]
    {:app_roles (:app_roles app)
     :app (dissoc app :app_roles)}))
    {:app-roles (:app-roles app)
     :app (dissoc app :app-roles)}))