~willvaughn/grsroot-auth

ref: 695bfe02e3f9b2c5da83444dfc601d34bbcc3454 grsroot-auth/src/clj/grsroot_auth/routes/services.clj -rw-r--r-- 10.2 KiB
695bfe02William Vaughn switch to kebab-case everywhere, auto-convert out of hugsql 5 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
(ns grsroot-auth.routes.services
  (:require
   [reitit.swagger :as swagger]
   [reitit.swagger-ui :as swagger-ui]
   [reitit.ring.coercion :as coercion]
   [reitit.coercion.spec :as spec-coercion]
   [reitit.ring.middleware.exception :as exception]
   [reitit.ring.middleware.muuntaja :as muuntaja]
   [reitit.ring.middleware.multipart :as multipart]
   [reitit.ring.middleware.parameters :as parameters]
   [grsroot-auth.config :refer [env]]
   [grsroot-auth.middleware :as middleware]
   [grsroot-auth.middleware.formats :as formats]
   [grsroot-auth.service :as svc]
   [ring.util.http-response :refer :all]
   [clojure.java.io :as io]))

(defn service-routes []
  ["/api"
   {:coercion spec-coercion/coercion
    :muuntaja formats/instance
    :swagger {:id ::api}
    :middleware [ ;; query-params & form-params
                 parameters/parameters-middleware
                 ;; content-negotiation
                 muuntaja/format-negotiate-middleware
                 ;; encoding response body
                 muuntaja/format-response-middleware
                 ;; auth
                 middleware/wrap-auth
                 ;; exception handling
                 exception/exception-middleware
                 ;; exception handling
                 coercion/coerce-exceptions-middleware
                 ;; decoding request body
                 muuntaja/format-request-middleware
                 ;; coercing response bodys
                 coercion/coerce-response-middleware
                 ;; coercing request parameters
                 coercion/coerce-request-middleware
                 ;; multipart
                 multipart/multipart-middleware]}

   ;; swagger documentation
   ["" {:no-doc true
        :swagger {:info {:title "Grassroot Recruiter - Auth Service"
                         :description "https://git.sr.ht/~willvaughn/grsroot-auth"}
                  :securityDefinitions {:apiAuth
                                        {:type "apiKey"
                                         :name "Authorization"
                                         :in "header"}}}}

    ["/swagger.json"
     {:get (swagger/create-swagger-handler)}]

    ["/api-docs/*"
     {:get (swagger-ui/create-swagger-ui-handler
            {:url "/api/swagger.json"
             :config {:validator-url nil}})}]]

   ["/create-auth-token"
    {:post {:id ::auth-token
            :summary "create a new authentication token for a user"
            :parameters {:body {:email string? :password string?}}
            :responses {201 {:body {:token string?}}
                        401 {:body {:message string?}}}
            :handler (fn [{{creds :body} :parameters}]
                       (let [[ok? res] (svc/create-auth-token (:auth-cfg env) creds)]
                         (if ok?
                           {:status 201 :body res}
                           {:status 401 :body res})))}}]

   ;; unauthenticated
   ;; TODO POST /api/register create new minimal user with email and pass, send a confirm link to email to acitvate the account.
   ;; https://github.com/drewr/postal
   ;; I don't think this app should send the email. This is not really "register" it's create. Registration is something the applications
   ;; 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 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
   ;;
   ;; Open
   ;; TODO GET /api/users/:app_user_id {:user {:app_user_id, :email, :created_at, :app_roles [{:app_role_id, :role_slug, :app {:app_id, :app_slug}}} }]}
   ;;
   ;; Open may not be necessary, just get the user
   ;; TODO GET /api/users/:app_user_id/roles
   ;;
   ;; Should be open, so applications can have functionality to alter user roles
   ;; TODO POST /api/users/:app_user_id/roles to add an app role to a user
   ;; OR TODO PUT /api/users/:app_user_id /
   ;;
   ;; Open, applications themselves should manage the change email and pw verification stuff themselves, then they update the user data through this service
   ;; TODO PUT /api/users/:app_user_id update new user optional pass users roles roles? Is this dfferent from register?
   ;; When I'm not storing anything about the user in this service...does this need to exist. Is this really just to update email address? If there is no reason to have this for a user, it should be only available to super-admin role.
   ["/app-users"
    {:swagger {:tags ["app-users"]}}
    ["" {:get {:id ::list-app-users
               :summary "List app users"
               :responses {200 {:body
                                {:app-users [{:app-user_id uuid?
                                              :email string?}]}}}
               :handler (fn [_]
                          (ok {:app-users []}))}
         :post {:id ::create-app-user
                :summary "Create/register a new app user"
                :responses {201 {:body {:app-user-id uuid?
                                        :email string?}}}
                :handler (fn [_]
                           (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?
                                       :email string?}}}
               :handler (fn [_]
                          (ok {:app-user-id (java.util.UUID/randomUUID)
                               :email "vaughnwilld@gmail.com"}))}}]]

   ["/apps"
    {:swagger {:tags ["apps"]}}
    ["" {: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?}]}]}}}
               :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?}}
                            401 {:body {:message string?}}
                            403 {:body {:message string?}}
                            409 {:body {:message string?}}}
                :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}))
                             (catch clojure.lang.ExceptionInfo e
                               (if (= (:grsroot-auth/error-id (ex-data e))
                                      ::svc/unique-violation)
                                 (conflict {:message (:error (ex-data e))})
                                 (throw e)))))}}]
    ["/:app-id"
     ["" {: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?}]}}
                            404 {:body {:message string?}}}
                :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?}}}
                                  404 {:body {:message string?}}}
                      :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!"})))}
                :post {:id ::create-app-role
                       :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?}}
                                   401 {:body {:message string?}}
                                   403 {:body {:message string?}}
                                   409 {:body {:message string?}}}
                       :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}))
                                    (catch clojure.lang.ExceptionInfo e
                                      (if (= (:grsroot-auth/error-id (ex-data e))
                                             ::svc/unique-violation)
                                        (conflict {:message (:error (ex-data e))})
                                        (throw e)))))}}]]]])