mirror of
https://github.com/penpot/penpot.git
synced 2025-03-18 10:41:29 -05:00
✨ Login with Gitlab
This commit is contained in:
parent
dcd7e0b3cc
commit
cc1353300e
9 changed files with 207 additions and 1 deletions
|
@ -98,6 +98,10 @@
|
|||
(s/def ::google-client-id ::us/string)
|
||||
(s/def ::google-client-secret ::us/string)
|
||||
|
||||
(s/def ::gitlab-client-id ::us/string)
|
||||
(s/def ::gitlab-client-secret ::us/string)
|
||||
(s/def ::gitlab-base-uri ::us/string)
|
||||
|
||||
(s/def ::ldap-auth-host ::us/string)
|
||||
(s/def ::ldap-auth-port ::us/integer)
|
||||
(s/def ::ldap-bind-dn ::us/string)
|
||||
|
@ -118,6 +122,9 @@
|
|||
::http-server-port
|
||||
::google-client-id
|
||||
::google-client-secret
|
||||
::gitlab-client-id
|
||||
::gitlab-client-secret
|
||||
::gitlab-base-uri
|
||||
::public-uri
|
||||
::database-username
|
||||
::database-password
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
[ring.adapter.jetty9 :as jetty]
|
||||
[app.config :as cfg]
|
||||
[app.http.auth :as auth]
|
||||
[app.http.auth.gitlab :as gitlab]
|
||||
[app.http.auth.google :as google]
|
||||
[app.http.auth.ldap :as ldap]
|
||||
[app.http.debug :as debug]
|
||||
|
@ -40,7 +41,9 @@
|
|||
|
||||
["/oauth"
|
||||
["/google" {:post google/auth}]
|
||||
["/google/callback" {:get google/callback}]]
|
||||
["/google/callback" {:get google/callback}]
|
||||
["/gitlab" {:post gitlab/auth}]
|
||||
["/gitlab/callback" {:get gitlab/callback}]]
|
||||
|
||||
["/echo" {:get handlers/echo-handler
|
||||
:post handlers/echo-handler}]
|
||||
|
|
153
backend/src/app/http/auth/gitlab.clj
Normal file
153
backend/src/app/http/auth/gitlab.clj
Normal file
|
@ -0,0 +1,153 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns app.http.auth.gitlab
|
||||
(:require
|
||||
[clojure.data.json :as json]
|
||||
[clojure.tools.logging :as log]
|
||||
[lambdaisland.uri :as uri]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.config :as cfg]
|
||||
[app.db :as db]
|
||||
[app.services.tokens :as tokens]
|
||||
[app.services.mutations :as sm]
|
||||
[app.http.session :as session]
|
||||
[app.util.http :as http]))
|
||||
|
||||
|
||||
(def default-base-gitlab-uri "https://gitlab.com")
|
||||
|
||||
|
||||
(def scope "read_user")
|
||||
|
||||
|
||||
(defn- build-redirect-url
|
||||
[]
|
||||
(let [public (uri/uri (:public-uri cfg/config))]
|
||||
(str (assoc public :path "/api/oauth/gitlab/callback"))))
|
||||
|
||||
|
||||
(defn- build-oauth-uri
|
||||
[]
|
||||
(let [base-uri (uri/uri (:gitlab-base-uri cfg/config default-base-gitlab-uri))]
|
||||
(assoc base-uri :path "/oauth/authorize")))
|
||||
|
||||
|
||||
(defn- build-token-url
|
||||
[]
|
||||
(let [base-uri (uri/uri (:gitlab-base-uri cfg/config default-base-gitlab-uri))]
|
||||
(str (assoc base-uri :path "/oauth/token"))))
|
||||
|
||||
|
||||
(defn- build-user-info-url
|
||||
[]
|
||||
(let [base-uri (uri/uri (:gitlab-base-uri cfg/config default-base-gitlab-uri))]
|
||||
(str (assoc base-uri :path "/api/v4/user"))))
|
||||
|
||||
|
||||
(defn- get-access-token
|
||||
[code]
|
||||
(let [params {:client_id (:gitlab-client-id cfg/config)
|
||||
:client_secret (:gitlab-client-secret cfg/config)
|
||||
:code code
|
||||
:grant_type "authorization_code"
|
||||
:redirect_uri (build-redirect-url)}
|
||||
req {:method :post
|
||||
:headers {"content-type" "application/x-www-form-urlencoded"}
|
||||
:uri (build-token-url)
|
||||
:body (uri/map->query-string params)}
|
||||
res (http/send! req)]
|
||||
|
||||
(when (not= 200 (:status res))
|
||||
(ex/raise :type :internal
|
||||
:code :invalid-response-from-gitlab
|
||||
:context {:status (:status res)
|
||||
:body (:body res)}))
|
||||
|
||||
(try
|
||||
(let [data (json/read-str (:body res))]
|
||||
(get data "access_token"))
|
||||
(catch Throwable e
|
||||
(log/error "unexpected error on parsing response body from gitlab access tooken request" e)
|
||||
nil))))
|
||||
|
||||
|
||||
(defn- get-user-info
|
||||
[token]
|
||||
(let [req {:uri (build-user-info-url)
|
||||
:headers {"Authorization" (str "Bearer " token)}
|
||||
:method :get}
|
||||
res (http/send! req)]
|
||||
|
||||
(when (not= 200 (:status res))
|
||||
(ex/raise :type :internal
|
||||
:code :invalid-response-from-gitlab
|
||||
:context {:status (:status res)
|
||||
:body (:body res)}))
|
||||
|
||||
(try
|
||||
(let [data (json/read-str (:body res))]
|
||||
;; (clojure.pprint/pprint data)
|
||||
{:email (get data "email")
|
||||
:fullname (get data "name")})
|
||||
(catch Throwable e
|
||||
(log/error "unexpected error on parsing response body from gitlab access tooken request" e)
|
||||
nil))))
|
||||
|
||||
|
||||
(defn auth
|
||||
[req]
|
||||
(let [token (tokens/create! db/pool {:type :gitlab-oauth})
|
||||
params {:client_id (:gitlab-client-id cfg/config)
|
||||
:redirect_uri (build-redirect-url)
|
||||
:response_type "code"
|
||||
:state token
|
||||
:scope scope}
|
||||
query (uri/map->query-string params)
|
||||
uri (-> (build-oauth-uri)
|
||||
(assoc :query query))]
|
||||
{:status 200
|
||||
:body {:redirect-uri (str uri)}}))
|
||||
|
||||
|
||||
(defn callback
|
||||
[req]
|
||||
(let [token (get-in req [:params :state])
|
||||
tdata (tokens/retrieve db/pool token)
|
||||
info (some-> (get-in req [:params :code])
|
||||
(get-access-token)
|
||||
(get-user-info))]
|
||||
|
||||
(when (not= :gitlab-oauth (:type tdata))
|
||||
(ex/raise :type :validation
|
||||
:code ::tokens/invalid-token))
|
||||
|
||||
(when-not info
|
||||
(ex/raise :type :authentication
|
||||
:code ::unable-to-authenticate-with-gitlab))
|
||||
|
||||
(let [profile (sm/handle {::sm/type :login-or-register
|
||||
:email (:email info)
|
||||
:fullname (:fullname info)})
|
||||
uagent (get-in req [:headers "user-agent"])
|
||||
|
||||
tdata {:type :authentication
|
||||
:profile profile}
|
||||
token (tokens/create! db/pool tdata {:valid {:minutes 10}})
|
||||
|
||||
uri (-> (uri/uri (:public-uri cfg/config))
|
||||
(assoc :path "/#/auth/verify-token")
|
||||
(assoc :query (uri/map->query-string {:token token})))
|
||||
sid (session/create (:id profile) uagent)]
|
||||
|
||||
{:status 302
|
||||
:headers {"location" (str uri)}
|
||||
:cookies (session/cookies sid)
|
||||
:body ""})))
|
||||
|
|
@ -163,6 +163,7 @@ function templatePipeline(options) {
|
|||
"var appLoginWithLDAP = null;",
|
||||
"var appPublicURI = null;",
|
||||
"var appGoogleClientID = null;",
|
||||
"var appGitlabClientID = null;",
|
||||
"var appDeployDate = null;",
|
||||
"var appDeployCommit = null;"
|
||||
];
|
||||
|
|
10
frontend/resources/images/icons/brand-gitlab.svg
Normal file
10
frontend/resources/images/icons/brand-gitlab.svg
Normal file
|
@ -0,0 +1,10 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 13.1072 13.10542" preserveAspectRatio="xMinYMin meet">
|
||||
<path d="M6.5534 13.1502l2.41333-7.42742H4.14l2.41334 7.42743z" fill="#e24329"/>
|
||||
<path d="M6.5534 13.15016L4.14 5.72273H.75783l5.79556 7.42743z" fill="#fc6d26"/>
|
||||
<path d="M.75783 5.72273L.02446 7.97991a.49964.49964 0 00.18147.5586l6.34746 4.6117L.75777 5.72278z" fill="#fca326"/>
|
||||
<path d="M.75783 5.72278H4.14L2.68654 1.24927c-.0748-.2302-.40045-.23015-.4752 0L.75783 5.72278z" fill="#e24329"/>
|
||||
<path d="M6.5534 13.15016l2.41333-7.42743h3.38223l-5.79562 7.42743z" fill="#fc6d26"/>
|
||||
<path d="M12.34896 5.72273l.73336 2.25718" fill="#fca326"/>
|
||||
<path d="M12.34896 5.72278H8.96673l1.45351-4.47351c.0748-.2302.40045-.23015.4752 0l1.45352 4.47351z" fill="#e24329"/>
|
||||
<path d="M12.34937 5.72273l.73337 2.25718a.49964.49964 0 01-.18147.5586l-6.34746 4.6117 5.79561-7.42742z" fill="#fca326"/>
|
||||
</svg>
|
After Width: | Height: | Size: 954 B |
|
@ -63,4 +63,15 @@
|
|||
margin-bottom: $medium;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-gitlab-auth {
|
||||
margin-bottom: $medium;
|
||||
text-decoration: none;
|
||||
|
||||
.logo {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
(def default-language "en")
|
||||
(def demo-warning (obj/get global "appDemoWarning" false))
|
||||
(def google-client-id (obj/get global "appGoogleClientID" nil))
|
||||
(def gitlab-client-id (obj/get global "appGitlabClientID" nil))
|
||||
(def login-with-ldap (obj/get global "appLoginWithLDAP" false))
|
||||
(def worker-uri (obj/get global "appWorkerURI" "/js/worker.js"))
|
||||
(def public-uri (or (obj/get global "appPublicURI")
|
||||
|
|
|
@ -71,6 +71,12 @@
|
|||
(->> (http/send! {:method :post :uri uri})
|
||||
(rx/mapcat handle-response))))
|
||||
|
||||
(defmethod mutation :login-with-gitlab
|
||||
[id params]
|
||||
(let [uri (str cfg/public-uri "/api/oauth/gitlab")]
|
||||
(->> (http/send! {:method :post :uri uri})
|
||||
(rx/mapcat handle-response))))
|
||||
|
||||
(defmethod mutation :upload-media-object
|
||||
[id params]
|
||||
(let [form (js/FormData.)]
|
||||
|
|
|
@ -40,6 +40,13 @@
|
|||
(rx/subs (fn [{:keys [redirect-uri] :as rsp}]
|
||||
(.replace js/location redirect-uri)))))
|
||||
|
||||
(defn- login-with-gitlab
|
||||
[event]
|
||||
(dom/prevent-default event)
|
||||
(->> (rp/mutation! :login-with-gitlab {})
|
||||
(rx/subs (fn [{:keys [redirect-uri] :as rsp}]
|
||||
(.replace js/location redirect-uri)))))
|
||||
|
||||
(mf/defc login-form
|
||||
[{:keys [locale] :as props}]
|
||||
(let [error? (mf/use-state false)
|
||||
|
@ -113,6 +120,13 @@
|
|||
{:on-click login-with-google}
|
||||
"Login with Google"])
|
||||
|
||||
(when cfg/gitlab-client-id
|
||||
[:a.btn-ocean.btn-large.btn-gitlab-auth
|
||||
{:on-click login-with-gitlab}
|
||||
[:img.logo
|
||||
{:src "/images/icons/brand-gitlab.svg"}]
|
||||
"Login with Gitlab"])
|
||||
|
||||
[:div.links.demo
|
||||
[:div.link-entry
|
||||
[:span (t locale "auth.create-demo-profile-label") " "]
|
||||
|
|
Loading…
Add table
Reference in a new issue