diff --git a/CHANGES.md b/CHANGES.md
index b6f35bad86..b8a720fb9c 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -6,6 +6,7 @@
### :sparkles: New features
- Set an artboard as the file thumbnail [Taiga #1526](https://tree.taiga.io/project/penpot/us/1526)
+- Social login redesign [Taiga #2974](https://tree.taiga.io/project/penpot/task/2974)
- Add border radius to our artboars [Taiga #2056](https://tree.taiga.io/project/penpot/us/2056)
- Allow send multiple team invitations at once [Taiga #2798](https://tree.taiga.io/project/penpot/us/2798)
- Persist color palette and color picker across refresh [Taiga #1660](https://tree.taiga.io/project/penpot/issue/1660)
@@ -18,6 +19,9 @@
- New focus mode in workspace [Taiga #2748](https://tree.taiga.io/project/penpot/us/2748)
- Changed text shapes to be displayed as natives SVG text elements [Taiga #2759](https://tree.taiga.io/project/penpot/us/2759)
- Texts now can have strokes, multiple fills and can be used as masks
+- Add the ability to specify the attr for retrieve the email on OIDC integration [#1460](https://github.com/penpot/penpot/issues/1460)
+- Allow registration with invitation token when registration is disabled
+- Add the ability to disable standard, password login [Taiga #2999](https://tree.taiga.io/project/penpot/us/2999)
### :bug: Bugs fixed
diff --git a/backend/src/app/config.clj b/backend/src/app/config.clj
index b11ceae63d..6dfbfc7ae4 100644
--- a/backend/src/app/config.clj
+++ b/backend/src/app/config.clj
@@ -90,7 +90,7 @@
(s/def ::flags ::us/set-of-keywords)
-;; DEPRECATED PROPERTIES: should be removed in 1.10
+;; DEPRECATED PROPERTIES
(s/def ::registration-enabled ::us/boolean)
(s/def ::smtp-enabled ::us/boolean)
(s/def ::telemetry-enabled ::us/boolean)
@@ -138,11 +138,15 @@
(s/def ::oidc-scopes ::us/set-of-str)
(s/def ::oidc-roles ::us/set-of-str)
(s/def ::oidc-roles-attr ::us/keyword)
+(s/def ::oidc-email-attr ::us/keyword)
+(s/def ::oidc-name-attr ::us/keyword)
(s/def ::host ::us/string)
(s/def ::http-server-port ::us/integer)
(s/def ::http-server-host ::us/string)
-(s/def ::http-server-min-threads ::us/integer)
-(s/def ::http-server-max-threads ::us/integer)
+(s/def ::http-server-max-body-size ::us/integer)
+(s/def ::http-server-max-multipart-body-size ::us/integer)
+(s/def ::http-server-io-threads ::us/integer)
+(s/def ::http-server-worker-threads ::us/integer)
(s/def ::http-session-idle-max-age ::dt/duration)
(s/def ::http-session-updater-batch-max-age ::dt/duration)
(s/def ::http-session-updater-batch-max-size ::us/integer)
@@ -239,12 +243,16 @@
::oidc-user-uri
::oidc-scopes
::oidc-roles-attr
+ ::oidc-email-attr
+ ::oidc-name-attr
::oidc-roles
::host
::http-server-host
::http-server-port
- ::http-server-max-threads
- ::http-server-min-threads
+ ::http-server-max-body-size
+ ::http-server-max-multipart-body-size
+ ::http-server-io-threads
+ ::http-server-worker-threads
::http-session-idle-max-age
::http-session-updater-batch-max-age
::http-session-updater-batch-max-size
@@ -339,8 +347,8 @@
(when (ex/ex-info? e)
(println ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;")
(println "Error on validating configuration:")
- (println (:explain (ex-data e))
- (println ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;")))
+ (println (us/pretty-explain (ex-data e)))
+ (println ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"))
(throw e))))
(def version
diff --git a/backend/src/app/http.clj b/backend/src/app/http.clj
index fa86438790..010bc941fa 100644
--- a/backend/src/app/http.clj
+++ b/backend/src/app/http.clj
@@ -7,9 +7,7 @@
(ns app.http
(:require
[app.common.data :as d]
- [app.common.exceptions :as ex]
[app.common.logging :as l]
- [app.common.spec :as us]
[app.http.doc :as doc]
[app.http.errors :as errors]
[app.http.middleware :as middleware]
@@ -31,40 +29,46 @@
(s/def ::handler fn?)
(s/def ::router some?)
-(s/def ::port ::us/integer)
-(s/def ::host ::us/string)
-(s/def ::name ::us/string)
-(s/def ::executors (s/map-of keyword? ::wrk/executor))
+(s/def ::port integer?)
+(s/def ::host string?)
+(s/def ::name string?)
-;; (s/def ::max-threads ::cf/http-server-max-threads)
-;; (s/def ::min-threads ::cf/http-server-min-threads)
+(s/def ::max-body-size integer?)
+(s/def ::max-multipart-body-size integer?)
+(s/def ::io-threads integer?)
+(s/def ::worker-threads integer?)
(defmethod ig/prep-key ::server
[_ cfg]
(merge {:name "http"
:port 6060
- :host "0.0.0.0"}
+ :host "0.0.0.0"
+ :max-body-size (* 1024 1024 24) ; 24 MiB
+ :max-multipart-body-size (* 1024 1024 120)} ; 120 MiB
(d/without-nils cfg)))
(defmethod ig/pre-init-spec ::server [_]
- (s/keys :req-un [::port ::host ::name ::executors]
- :opt-un [::router ::handler]))
+ (s/and
+ (s/keys :req-un [::port ::host ::name ::max-body-size ::max-multipart-body-size]
+ :opt-un [::router ::handler ::io-threads ::worker-threads ::wrk/executor])
+ (fn [cfg]
+ (or (contains? cfg :router)
+ (contains? cfg :handler)))))
(defmethod ig/init-key ::server
- [_ {:keys [handler router port name host executors] :as cfg}]
- (l/info :hint "starting http server"
- :port port :host host :name name)
-
+ [_ {:keys [handler router port name host] :as cfg}]
+ (l/info :hint "starting http server" :port port :host host :name name)
(let [options {:http/port port
:http/host host
- :ring/async true
- :xnio/dispatch (:default executors)}
- handler (cond
- (fn? handler) handler
- (some? router) (wrap-router cfg router)
- :else (ex/raise :type :internal
- :code :invalid-argument
- :hint "Missing `handler` or `router` option."))
+ :http/max-body-size (:max-body-size cfg)
+ :http/max-multipart-body-size (:max-multipart-body-size cfg)
+ :xnio/io-threads (:io-threads cfg)
+ :xnio/worker-threads (:worker-threads cfg)
+ :xnio/dispatch (:executor cfg)
+ :ring/async true}
+ handler (if (some? router)
+ (wrap-router cfg router)
+ handler)
server (yt/server handler (d/without-nils options))]
(assoc cfg :server (yt/start! server))))
@@ -97,7 +101,7 @@
(handler request respond
(fn [cause]
(l/error :hint "unexpected error processing request"
- ::l/context (errors/get-error-context request cause)
+ ::l/context (errors/get-context request)
:query-string (yrq/query request)
:cause cause)
(respond (yrs/response 500 "internal server error")))))))
diff --git a/backend/src/app/http/debug.clj b/backend/src/app/http/debug.clj
index 53f70a584e..92daf1f63a 100644
--- a/backend/src/app/http/debug.clj
+++ b/backend/src/app/http/debug.clj
@@ -162,7 +162,8 @@
(let [context (dissoc report
:trace :cause :params :data :spec-problems
:spec-explain :spec-value :error :explain :hint)
- params {:context (with-out-str (fpp/pprint context {:width 300}))
+ params {:context (with-out-str
+ (fpp/pprint context {:width 200}))
:hint (:hint report)
:spec-explain (:spec-explain report)
:spec-problems (:spec-problems report)
diff --git a/backend/src/app/http/errors.clj b/backend/src/app/http/errors.clj
index 40eaf9fc31..95d4f9819a 100644
--- a/backend/src/app/http/errors.clj
+++ b/backend/src/app/http/errors.clj
@@ -21,27 +21,17 @@
(yrq/get-header request "x-real-ip")
(yrq/remote-addr request)))
-(defn get-error-context
- [request error]
- (let [data (ex-data error)]
- (merge
- {:path (:uri request)
- :method (:request-method request)
- :hint (ex-message error)
- :params (:params request)
-
- :spec-problems (some->> data ::s/problems (take 10) seq vec)
- :spec-value (some->> data ::s/value)
- :data (some-> data (dissoc ::s/problems ::s/value ::s/spec))
- :ip-addr (parse-client-ip request)
- :profile-id (:profile-id request)}
-
- (let [headers (:headers request)]
- {:user-agent (get headers "user-agent")
- :frontend-version (get headers "x-frontend-version" "unknown")})
-
- (when (and data (::s/problems data))
- {:spec-explain (us/pretty-explain data)}))))
+(defn get-context
+ [request]
+ (merge
+ {:path (:uri request)
+ :method (:request-method request)
+ :params (:params request)
+ :ip-addr (parse-client-ip request)
+ :profile-id (:profile-id request)}
+ (let [headers (:headers request)]
+ {:user-agent (get headers "user-agent")
+ :frontend-version (get headers "x-frontend-version" "unknown")})))
(defmulti handle-exception
(fn [err & _rest]
@@ -70,7 +60,7 @@
(let [edata (ex-data error)
explain (us/pretty-explain edata)]
(l/error ::l/raw (ex-message error)
- ::l/context (get-error-context request error)
+ ::l/context (get-context request)
:cause error)
(yrs/response :status 500
:body {:type :server-error
@@ -96,7 +86,7 @@
(handle-exception (:handling edata) request)
(do
(l/error ::l/raw (ex-message error)
- ::l/context (get-error-context request error)
+ ::l/context (get-context request)
:cause error)
(yrs/response 500 {:type :server-error
:code :unexpected
@@ -107,7 +97,7 @@
[error request]
(let [state (.getSQLState ^java.sql.SQLException error)]
(l/error ::l/raw (ex-message error)
- ::l/context (get-error-context request error)
+ ::l/context (get-context request)
:cause error)
(cond
(= state "57014")
diff --git a/backend/src/app/http/oauth.clj b/backend/src/app/http/oauth.clj
index fffdcfbd14..4e27485293 100644
--- a/backend/src/app/http/oauth.clj
+++ b/backend/src/app/http/oauth.clj
@@ -57,7 +57,8 @@
:grant_type "authorization_code"
:redirect_uri (build-redirect-uri cfg)}
req {:method :post
- :headers {"content-type" "application/x-www-form-urlencoded"}
+ :headers {"content-type" "application/x-www-form-urlencoded"
+ "accept" "application/json"}
:uri (:token-uri provider)
:body (u/map->query-string params)}]
(p/then
@@ -69,42 +70,61 @@
:type (get data :token_type)})
(ex/raise :type :internal
:code :unable-to-retrieve-token
- ::http-status status
- ::http-body body))))))
+ :http-status status
+ :http-body body))))))
(defn- retrieve-user-info
[{:keys [provider http-client] :as cfg} tdata]
- (p/then
- (http-client {:uri (:user-uri provider)
- :headers {"Authorization" (str (:type tdata) " " (:token tdata))}
- :timeout 6000
- :method :get})
- (fn [{:keys [status body] :as res}]
- (if (= 200 status)
- (let [info (json/read body)
- info {:backend (:name provider)
- :email (get info :email)
- :fullname (get info :name)
- :props (->> (dissoc info :name :email)
- (qualify-props provider))}]
+ (letfn [(retrieve []
+ (http-client {:uri (:user-uri provider)
+ :headers {"Authorization" (str (:type tdata) " " (:token tdata))}
+ :timeout 6000
+ :method :get}))
- (when-not (s/valid? ::info info)
- (l/warn :hint "received incomplete profile info object (please set correct scopes)"
- :info (pr-str info))
- (ex/raise :type :internal
- :code :unable-to-auth
- :hint "no user info"))
- info)
- (ex/raise :type :internal
- :code :unable-to-retrieve-user-info
- ::http-status status
- ::http-body body)))))
+ (validate-response [{:keys [status body] :as res}]
+ (when-not (= 200 status)
+ (ex/raise :type :internal
+ :code :unable-to-retrieve-user-info
+ :hint "unable to retrieve user info"
+ :http-status status
+ :http-body body))
+ res)
+
+ (get-email [info]
+ (let [attr-kw (cf/get :oidc-email-attr :email)]
+ (get info attr-kw)))
+
+ (get-name [info]
+ (let [attr-kw (cf/get :oidc-name-attr :name)]
+ (get info attr-kw)))
+
+ (process-response [{:keys [body]}]
+ (let [info (json/read body)]
+ {:backend (:name provider)
+ :email (get-email info)
+ :fullname (get-name info)
+ :props (->> (dissoc info :name :email)
+ (qualify-props provider))}))
+
+ (validate-info [info]
+ (when-not (s/valid? ::info info)
+ (l/warn :hint "received incomplete profile info object (please set correct scopes)"
+ :info (pr-str info))
+ (ex/raise :type :internal
+ :code :incomplete-user-info
+ :hint "inconmplete user info"
+ :info info))
+ info)]
+
+ (-> (retrieve)
+ (p/then' validate-response)
+ (p/then' process-response)
+ (p/then' validate-info))))
(s/def ::backend ::us/not-empty-string)
(s/def ::email ::us/not-empty-string)
(s/def ::fullname ::us/not-empty-string)
(s/def ::props (s/map-of ::us/keyword any?))
-
(s/def ::info
(s/keys :req-un [::backend
::email
@@ -112,7 +132,7 @@
::props]))
(defn retrieve-info
- [{:keys [tokens provider] :as cfg} request]
+ [{:keys [tokens provider] :as cfg} {:keys [params] :as request}]
(letfn [(validate-oidc [info]
;; If the provider is OIDC, we can proceed to check
;; roles if they are defined.
@@ -143,9 +163,15 @@
(map? (:props state))
(update :props merge (:props state))))]
- (let [state (get-in request [:params :state])
- state (tokens :verify {:token state :iss :oauth})
- code (get-in request [:params :code])]
+ (when-let [error (get params :error)]
+ (ex/raise :type :internal
+ :code :error-on-retrieving-code
+ :error-id error
+ :error-desc (get params :error_description)))
+
+ (let [state (get params :state)
+ code (get params :code)
+ state (tokens :verify {:token state :iss :oauth})]
(-> (p/resolved code)
(p/then #(retrieve-access-token cfg %))
(p/then #(retrieve-user-info cfg %))
@@ -224,15 +250,18 @@
(redirect-response uri))))
(defn- auth-handler
- [{:keys [tokens] :as cfg} {:keys [params] :as request} respond _]
- (let [props (extract-utm-props params)
- state (tokens :generate
- {:iss :oauth
- :invitation-token (:invitation-token params)
- :props props
- :exp (dt/in-future "15m")})
- uri (build-auth-uri cfg state)]
- (respond (yrs/response 200 {:redirect-uri uri}))))
+ [{:keys [tokens] :as cfg} {:keys [params] :as request} respond raise]
+ (try
+ (let [props (extract-utm-props params)
+ state (tokens :generate
+ {:iss :oauth
+ :invitation-token (:invitation-token params)
+ :props props
+ :exp (dt/in-future "15m")})
+ uri (build-auth-uri cfg state)]
+ (respond (yrs/response 200 {:redirect-uri uri})))
+ (catch Throwable cause
+ (raise cause))))
(defn- callback-handler
[cfg request respond _]
@@ -242,7 +271,7 @@
(generate-redirect cfg request info profile)))
(handle-error [cause]
- (l/warn :hint "error on oauth process" :cause cause)
+ (l/error :hint "error on oauth process" :cause cause)
(respond (generate-error-redirect cfg cause)))]
(-> (process-request)
@@ -385,17 +414,16 @@
(assoc-in cfg [:providers "github"] opts))
cfg)))
-
(defn- initialize-gitlab-provider
[cfg]
(let [base (cf/get :gitlab-base-uri "https://gitlab.com")
opts {:base-uri base
:client-id (cf/get :gitlab-client-id)
:client-secret (cf/get :gitlab-client-secret)
- :scopes #{"read_user"}
+ :scopes #{"openid" "profile" "email"}
:auth-uri (str base "/oauth/authorize")
:token-uri (str base "/oauth/token")
- :user-uri (str base "/api/v4/user")
+ :user-uri (str base "/oauth/userinfo")
:name "gitlab"}]
(if (and (string? (:client-id opts))
(string? (:client-secret opts)))
diff --git a/backend/src/app/loggers/audit.clj b/backend/src/app/loggers/audit.clj
index ae80ffaec2..952c4d640d 100644
--- a/backend/src/app/loggers/audit.clj
+++ b/backend/src/app/loggers/audit.clj
@@ -254,7 +254,7 @@
"select * from audit_log
where archived_at is null
order by created_at asc
- limit 1000
+ limit 256
for update skip locked;")
(defn archive-events
@@ -298,8 +298,9 @@
(if (= (:status resp) 204)
true
(do
- (l/warn :hint "unable to archive events"
- :resp-status (:status resp))
+ (l/error :hint "unable to archive events"
+ :resp-status (:status resp)
+ :resp-body (:body resp))
false))))
(mark-as-archived [conn rows]
diff --git a/backend/src/app/main.clj b/backend/src/app/main.clj
index 7b54ad9f7e..ec0808a8c6 100644
--- a/backend/src/app/main.clj
+++ b/backend/src/app/main.clj
@@ -113,7 +113,8 @@
:host (cf/get :http-server-host)
:router (ig/ref :app.http/router)
:metrics (ig/ref :app.metrics/metrics)
- :executors (ig/ref :app.worker/executors)}
+ :executor (ig/ref [::default :app.worker/executor])
+ :io-threads (cf/get :http-server-io-threads)}
:app.http/router
{:assets (ig/ref :app.http.assets/handlers)
diff --git a/backend/src/app/metrics.clj b/backend/src/app/metrics.clj
index 254c9d56b4..2ae0223dc6 100644
--- a/backend/src/app/metrics.clj
+++ b/backend/src/app/metrics.clj
@@ -262,10 +262,3 @@
:gauge (make-gauge props)
:summary (make-summary props)
:histogram (make-histogram props)))
-
-;; (defn instrument-jetty!
-;; [^CollectorRegistry registry ^StatisticsHandler handler]
-;; (doto (JettyStatisticsCollector. handler)
-;; (.register registry))
-;; nil)
-
diff --git a/backend/src/app/rpc/mutations/profile.clj b/backend/src/app/rpc/mutations/profile.clj
index 540e5e22b5..963c364160 100644
--- a/backend/src/app/rpc/mutations/profile.clj
+++ b/backend/src/app/rpc/mutations/profile.clj
@@ -19,7 +19,6 @@
[app.rpc.queries.profile :as profile]
[app.rpc.rlimit :as rlimit]
[app.storage :as sto]
- [app.util.async :as async]
[app.util.services :as sv]
[app.util.time :as dt]
[buddy.hashers :as hashers]
@@ -101,8 +100,14 @@
(sv/defmethod ::prepare-register-profile {:auth false}
[{:keys [pool tokens] :as cfg} params]
(when-not (contains? cf/flags :registration)
- (ex/raise :type :restriction
- :code :registration-disabled))
+ (if-not (contains? params :invitation-token)
+ (ex/raise :type :restriction
+ :code :registration-disabled)
+ (let [invitation (tokens :verify {:token (:invitation-token params) :iss :team-invitation})]
+ (when-not (= (:email params) (:member-email invitation))
+ (ex/raise :type :restriction
+ :code :email-does-not-match-invitation
+ :hint "email should match the invitation")))))
(when-let [domains (cf/get :registration-domain-whitelist)]
(when-not (email-domain-in-whitelist? domains (:email params))
@@ -129,6 +134,7 @@
:backend "penpot"
:iss :prepared-register
:exp (dt/in-future "48h")}
+
token (tokens :generate params)]
{:token token}))
@@ -149,7 +155,6 @@
[{:keys [conn tokens session] :as cfg} {:keys [token] :as params}]
(let [claims (tokens :verify {:token token :iss :prepared-register})
params (merge params claims)]
-
(check-profile-existence! conn params)
(let [is-active (or (:is-active params)
@@ -158,10 +163,8 @@
(create-profile conn)
(create-profile-relations conn)
(decode-profile-row))
-
invitation (when-let [token (:invitation-token params)]
(tokens :verify {:token token :iss :team-invitation}))]
-
(cond
;; If invitation token comes in params, this is because the user comes from team-invitation process;
;; in this case, regenerate token and send back to the user a new invitation token (and mark current
@@ -280,10 +283,14 @@
:opt-un [::scope ::invitation-token]))
(sv/defmethod ::login
- {:auth false
- ::async/dispatch :default
- ::rlimit/permits (cf/get :rlimit-password)}
+ {:auth false ::rlimit/permits (cf/get :rlimit-password)}
[{:keys [pool session tokens] :as cfg} {:keys [email password] :as params}]
+
+ (when-not (contains? cf/flags :login)
+ (ex/raise :type :restriction
+ :code :login-disabled
+ :hint "login is disabled in this instance"))
+
(letfn [(check-password [profile password]
(when (= (:password profile) "!")
(ex/raise :type :validation
diff --git a/backend/src/app/storage.clj b/backend/src/app/storage.clj
index cff6c3363a..2188351492 100644
--- a/backend/src/app/storage.clj
+++ b/backend/src/app/storage.clj
@@ -259,7 +259,7 @@
;; A task responsible to permanently delete already marked as deleted
;; storage files. The storage objects are practically never marked to
;; be deleted directly by the api call. The touched-gc is responsible
-;; collect the usage of the object and mark it as deleted.
+;; of collecting the usage of the object and mark it as deleted.
(declare sql:retrieve-deleted-objects-chunk)
diff --git a/backend/test/app/services_profile_test.clj b/backend/test/app/services_profile_test.clj
index fb0f4980e7..c87e7cfa8f 100644
--- a/backend/test/app/services_profile_test.clj
+++ b/backend/test/app/services_profile_test.clj
@@ -7,6 +7,7 @@
(ns app.services-profile-test
(:require
[app.common.uuid :as uuid]
+ [app.config :as cf]
[app.db :as db]
[app.rpc.mutations.profile :as profile]
[app.test-helpers :as th]
@@ -195,6 +196,56 @@
(t/is (nil? error))))
))
+(t/deftest prepare-and-register-with-invitation-and-disabled-registration-1
+ (with-redefs [app.config/flags [:disable-registration]]
+ (let [tokens-fn (:app.tokens/tokens th/*system*)
+ itoken (tokens-fn :generate
+ {:iss :team-invitation
+ :exp (dt/in-future "48h")
+ :role :editor
+ :team-id uuid/zero
+ :member-email "user@example.com"})
+ data {::th/type :prepare-register-profile
+ :invitation-token itoken
+ :email "user@example.com"
+ :password "foobar"}
+
+ {:keys [result error] :as out} (th/mutation! data)]
+ (t/is (nil? error))
+ (t/is (map? result))
+ (t/is (string? (:token result)))
+
+ (let [rtoken (:token result)
+ data {::th/type :register-profile
+ :token rtoken
+ :fullname "foobar"}
+
+ {:keys [result error] :as out} (th/mutation! data)]
+ ;; (th/print-result! out)
+ (t/is (nil? error))
+ (t/is (map? result))
+ (t/is (string? (:invitation-token result)))))))
+
+(t/deftest prepare-and-register-with-invitation-and-disabled-registration-2
+ (with-redefs [app.config/flags [:disable-registration]]
+ (let [tokens-fn (:app.tokens/tokens th/*system*)
+ itoken (tokens-fn :generate
+ {:iss :team-invitation
+ :exp (dt/in-future "48h")
+ :role :editor
+ :team-id uuid/zero
+ :member-email "user2@example.com"})
+
+ data {::th/type :prepare-register-profile
+ :invitation-token itoken
+ :email "user@example.com"
+ :password "foobar"}
+ {:keys [result error] :as out} (th/mutation! data)]
+ (t/is (th/ex-info? error))
+ (t/is (= :restriction (th/ex-type error)))
+ (t/is (= :email-does-not-match-invitation (th/ex-code error))))))
+
+
(t/deftest prepare-register-with-registration-disabled
(th/with-mocks {#'app.config/flags nil}
(let [data {::th/type :prepare-register-profile
diff --git a/backend/test/app/test_helpers.clj b/backend/test/app/test_helpers.clj
index f51bccc863..e626f4dd19 100644
--- a/backend/test/app/test_helpers.clj
+++ b/backend/test/app/test_helpers.clj
@@ -313,6 +313,14 @@
[v]
(instance? clojure.lang.ExceptionInfo v))
+(defn ex-type
+ [e]
+ (:type (ex-data e)))
+
+(defn ex-code
+ [e]
+ (:code (ex-data e)))
+
(defn ex-of-type?
[e type]
(let [data (ex-data e)]
diff --git a/common/src/app/common/exceptions.cljc b/common/src/app/common/exceptions.cljc
index dbc63a8ba7..b3b73e5a9f 100644
--- a/common/src/app/common/exceptions.cljc
+++ b/common/src/app/common/exceptions.cljc
@@ -23,11 +23,12 @@
::cause]))
(defn error
- [& {:keys [hint cause ::data] :as params}]
+ [& {:keys [hint cause ::data type] :as params}]
(s/assert ::error-params params)
(let [payload (-> params
(dissoc :cause ::data)
- (merge data))]
+ (merge data))
+ hint (or hint (pr-str type))]
(ex-info hint payload cause)))
(defmacro raise
diff --git a/common/src/app/common/flags.cljc b/common/src/app/common/flags.cljc
index 149f71c651..2bdaab8231 100644
--- a/common/src/app/common/flags.cljc
+++ b/common/src/app/common/flags.cljc
@@ -12,7 +12,7 @@
(def default
"A common flags that affects both: backend and frontend."
[:enable-registration
- :enable-demo-users])
+ :enable-login])
(defn parse
[& flags]
diff --git a/common/src/app/common/logging.cljc b/common/src/app/common/logging.cljc
index 663f4beae6..44cadc35d9 100644
--- a/common/src/app/common/logging.cljc
+++ b/common/src/app/common/logging.cljc
@@ -8,8 +8,10 @@
(:require
[app.common.exceptions :as ex]
[app.common.uuid :as uuid]
+ [app.common.spec :as us]
[clojure.pprint :refer [pprint]]
[cuerdas.core :as str]
+ [clojure.spec.alpha :as s]
[fipp.edn :as fpp]
#?(:clj [io.aviso.exception :as ie])
#?(:cljs [goog.log :as glog]))
@@ -152,6 +154,18 @@
[logger level]
(.isEnabled ^Logger logger ^Level level)))
+#?(:clj
+ (defn get-error-context
+ [error]
+ (when-let [data (ex-data error)]
+ (merge
+ {:hint (ex-message error)
+ :spec-problems (some->> data ::s/problems (take 10) seq vec)
+ :spec-value (some->> data ::s/value)
+ :data (some-> data (dissoc ::s/problems ::s/value ::s/spec))}
+ (when (and data (::s/problems data))
+ {:spec-explain (us/pretty-explain data)})))))
+
(defmacro log
[& {:keys [level cause ::logger ::async ::raw ::context] :or {async true} :as props}]
(if (:ns &env) ; CLJS
@@ -169,7 +183,9 @@
~(if async
`(send-off logging-agent
(fn [_#]
- (with-context (into {:id (uuid/next)} ~context)
+ (with-context (merge {:id (uuid/next)}
+ (get-error-context ~cause)
+ ~context)
(->> (or ~raw (build-map-message ~props))
(write-log! ~logger-sym ~level-sym ~cause)))))
diff --git a/common/src/app/common/spec.cljc b/common/src/app/common/spec.cljc
index 083011b7b6..03f3ddc37a 100644
--- a/common/src/app/common/spec.cljc
+++ b/common/src/app/common/spec.cljc
@@ -140,22 +140,26 @@
;; --- SPEC: set of Keywords
-(s/def ::set-of-keywords
- (s/conformer
- (fn [s]
- (let [xform (comp
- (map (fn [s]
- (cond
- (string? s) (keyword s)
- (keyword? s) s
- :else nil)))
- (filter identity))]
- (cond
- (set? s) (into #{} xform s)
- (string? s) (into #{} xform (str/words s))
- :else ::s/invalid)))
- (fn [s]
- (str/join " " (map name s)))))
+(letfn [(conform-fn [dest s]
+ (let [xform (keep (fn [s]
+ (cond
+ (string? s) (keyword s)
+ (keyword? s) s
+ :else nil)))]
+ (cond
+ (set? s) (into dest xform s)
+ (string? s) (into dest xform (str/words s))
+ :else ::s/invalid)))]
+
+ (s/def ::set-of-keywords
+ (s/conformer
+ (fn [s] (conform-fn #{} s))
+ (fn [s] (str/join " " (map name s)))))
+
+ (s/def ::vec-of-keywords
+ (s/conformer
+ (fn [s] (conform-fn [] s))
+ (fn [s] (str/join " " (map name s))))))
;; --- SPEC: email
diff --git a/frontend/resources/images/icons/brand-gitlab.svg b/frontend/resources/images/icons/brand-gitlab.svg
index 8dc42e8153..04993577bd 100644
--- a/frontend/resources/images/icons/brand-gitlab.svg
+++ b/frontend/resources/images/icons/brand-gitlab.svg
@@ -1,10 +1 @@
-
+
\ No newline at end of file
diff --git a/frontend/resources/images/icons/brand-google.svg b/frontend/resources/images/icons/brand-google.svg
new file mode 100644
index 0000000000..dba95de0fe
--- /dev/null
+++ b/frontend/resources/images/icons/brand-google.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/resources/images/icons/brand-openid.svg b/frontend/resources/images/icons/brand-openid.svg
new file mode 100644
index 0000000000..a335b66642
--- /dev/null
+++ b/frontend/resources/images/icons/brand-openid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/resources/styles/main/layouts/login.scss b/frontend/resources/styles/main/layouts/login.scss
index fa79a39958..5743434852 100644
--- a/frontend/resources/styles/main/layouts/login.scss
+++ b/frontend/resources/styles/main/layouts/login.scss
@@ -72,20 +72,44 @@
width: 412px;
.auth-buttons {
- margin-top: $size-6;
+ margin: $size-6 0 $size-4 0;
+ display: flex;
+ justify-content: center;
+ column-gap: 17px;
}
form {
- margin: 2rem 0;
+ margin: 2rem 0 0.5rem 0;
}
}
+ .btn-large {
+ flex-grow: 1;
+ font-size: 14px;
+ font-family: sourcesanspro;
+ font-style: normal;
+ font-weight: normal;
+ }
+
.btn-google-auth {
+ background-color: #4285f4;
+ color: $color-white;
margin-bottom: $size-4;
text-decoration: none;
+ .logo {
+ width: 20px;
+ height: 20px;
+ margin-right: 1rem;
+ }
+ &:hover {
+ background-color: #2065d7;
+ color: $color-white;
+ }
}
.btn-gitlab-auth {
+ background-color: #fc6d26;
+ color: $color-white;
margin-bottom: $size-4;
text-decoration: none;
@@ -94,9 +118,16 @@
height: 20px;
margin-right: 1rem;
}
+
+ &:hover {
+ background-color: #ee5f18;
+ color: $color-white;
+ }
}
.btn-github-auth {
+ background-color: #4c4c4c;
+ color: $color-white;
margin-bottom: $size-4;
text-decoration: none;
@@ -105,6 +136,15 @@
height: 20px;
margin-right: 1rem;
}
+
+ &:hover {
+ background-color: #2f2f2f;
+ color: $color-white;
+ }
+ }
+
+ .link-oidc {
+ text-align: center;
}
.separator {
@@ -112,6 +152,18 @@
justify-content: center;
width: 100%;
text-transform: uppercase;
+ text-align: center;
+
+ .text {
+ margin: 0 10px;
+ color: $color-gray-40;
+ }
+
+ .line {
+ border: 1px solid $color-gray-10;
+ flex-grow: 10;
+ margin: auto;
+ }
}
.links {
diff --git a/frontend/src/app/config.cljs b/frontend/src/app/config.cljs
index 58b03b2998..456f163a78 100644
--- a/frontend/src/app/config.cljs
+++ b/frontend/src/app/config.cljs
@@ -86,6 +86,9 @@
(def browser (atom (parse-browser)))
(def platform (atom (parse-platform)))
+(def terms-of-service-uri (obj/get global "penpotTermsOfServiceURI" nil))
+(def privacy-policy-uri (obj/get global "penpotPrivacyPolicyURI" nil))
+
;; maintain for backward compatibility
(let [login-with-ldap (obj/get global "penpotLoginWithLDAP" false)
registration (obj/get global "penpotRegistrationEnabled" true)]
@@ -135,5 +138,3 @@
(str (cond-> (u/join public-uri "assets/by-file-media-id/")
(true? thumbnail?) (u/join (str id "/thumbnail"))
(false? thumbnail?) (u/join (str id))))))
-
-
diff --git a/frontend/src/app/main/ui/auth.cljs b/frontend/src/app/main/ui/auth.cljs
index 9c79ed2ddb..3b00adacbb 100644
--- a/frontend/src/app/main/ui/auth.cljs
+++ b/frontend/src/app/main/ui/auth.cljs
@@ -6,6 +6,7 @@
(ns app.main.ui.auth
(:require
+ [app.config :as cf]
[app.main.ui.auth.login :refer [login-page]]
[app.main.ui.auth.recovery :refer [recovery-page]]
[app.main.ui.auth.recovery-request :refer [recovery-request-page]]
@@ -15,6 +16,23 @@
[app.util.i18n :as i18n :refer [tr]]
[rumext.alpha :as mf]))
+(mf/defc terms-login
+ []
+ (let [show-all? (and cf/terms-of-service-uri cf/privacy-policy-uri)
+ show-terms? (some? cf/terms-of-service-uri)
+ show-privacy? (some? cf/privacy-policy-uri)]
+
+ (when show-all?
+ [:div.terms-login
+ (when show-terms?
+ [:a {:href cf/terms-of-service-uri :target "_blank"} "Terms of service"])
+
+ (when show-all?
+ [:span "and"])
+
+ (when show-privacy?
+ [:a {:href cf/privacy-policy-uri :target "_blank"} "Privacy policy"])])))
+
(mf/defc auth
[{:keys [route] :as props}]
(let [section (get-in route [:data :name])
@@ -48,7 +66,5 @@
:auth-recovery
[:& recovery-page {:params params}])
- [:div.terms-login
- [:a {:href "https://penpot.app/terms.html" :target "_blank"} "Terms of service"]
- [:span "and"]
- [:a {:href "https://penpot.app/privacy.html" :target "_blank"} "Privacy policy"]]]]))
+ [:& terms-login {}]]]))
+
diff --git a/frontend/src/app/main/ui/auth/login.cljs b/frontend/src/app/main/ui/auth/login.cljs
index 936d5ebb0d..41e2c7e712 100644
--- a/frontend/src/app/main/ui/auth/login.cljs
+++ b/frontend/src/app/main/ui/auth/login.cljs
@@ -97,7 +97,6 @@
(login-with-ldap event (with-meta params
{:on-error on-error
:on-success on-succes})))))]
-
[:*
(when-let [message @error]
[:& msgs/inline-banner
@@ -123,9 +122,10 @@
:label (tr "auth.password")}]]
[:div.buttons-stack
- [:& fm/submit-button
- {:label (tr "auth.login-submit")
- :data-test "login-submit"}]
+ (when (contains? @cf/flags :login)
+ [:& fm/submit-button
+ {:label (tr "auth.login-submit")
+ :data-test "login-submit"}])
(when (contains? @cf/flags :login-with-ldap)
[:& fm/submit-button
@@ -136,50 +136,69 @@
[{:keys [params] :as props}]
[:div.auth-buttons
(when cf/google-client-id
- [:a.btn-ocean.btn-large.btn-google-auth
+ [:a.btn-primary.btn-large.btn-google-auth
{:on-click #(login-with-oauth % :google params)}
+ [:span.logo i/brand-google]
(tr "auth.login-with-google-submit")])
- (when cf/gitlab-client-id
- [:a.btn-ocean.btn-large.btn-gitlab-auth
- {:on-click #(login-with-oauth % :gitlab params)}
- [:img.logo
- {:src "/images/icons/brand-gitlab.svg"}]
- (tr "auth.login-with-gitlab-submit")])
-
(when cf/github-client-id
- [:a.btn-ocean.btn-large.btn-github-auth
+ [:a.btn-primary.btn-large.btn-github-auth
{:on-click #(login-with-oauth % :github params)}
- [:img.logo
- {:src "/images/icons/brand-github.svg"}]
+ [:span.logo i/brand-github]
(tr "auth.login-with-github-submit")])
+ (when cf/gitlab-client-id
+ [:a.btn-primary.btn-large.btn-gitlab-auth
+ {:on-click #(login-with-oauth % :gitlab params)}
+ [:span.logo i/brand-gitlab]
+ (tr "auth.login-with-gitlab-submit")])
+
(when cf/oidc-client-id
- [:a.btn-ocean.btn-large.btn-github-auth
+ [:a.btn-primary.btn-large.btn-github-auth
{:on-click #(login-with-oauth % :oidc params)}
+ [:span.logo i/brand-openid]
(tr "auth.login-with-oidc-submit")])])
+(mf/defc login-button-oidc
+ [{:keys [params] :as props}]
+ (when cf/oidc-client-id
+ [:div.link-entry.link-oidc
+ [:a {:on-click #(login-with-oauth % :oidc params)}
+ (tr "auth.login-with-oidc-submit")]]))
+
(mf/defc login-page
[{:keys [params] :as props}]
[:div.generic-form.login-form
[:div.form-container
[:h1 {:data-test "login-title"} (tr "auth.login-title")]
- [:div.subtitle (tr "auth.login-subtitle")]
-
- [:& login-form {:params params}]
(when show-alt-login-buttons?
[:*
- [:span.separator (tr "labels.or")]
+ [:span.separator
+ [:span.line]
+ [:span.text (tr "labels.continue-with")]
+ [:span.line]]
[:div.buttons
- [:& login-buttons {:params params}]]])
+ [:& login-buttons {:params params}]]
+
+ (when (or (contains? @cf/flags :login)
+ (contains? @cf/flags :login-with-ldap))
+ [:span.separator
+ [:span.line]
+ [:span.text (tr "labels.or")]
+ [:span.line]])])
+
+ (when (or (contains? @cf/flags :login)
+ (contains? @cf/flags :login-with-ldap))
+ [:& login-form {:params params}])
[:div.links
- [:div.link-entry
- [:a {:on-click #(st/emit! (rt/nav :auth-recovery-request))
- :data-test "forgot-password"}
- (tr "auth.forgot-password")]]
+ (when (contains? @cf/flags :login)
+ [:div.link-entry
+ [:a {:on-click #(st/emit! (rt/nav :auth-recovery-request))
+ :data-test "forgot-password"}
+ (tr "auth.forgot-password")]])
(when (contains? @cf/flags :registration)
[:div.link-entry
diff --git a/frontend/src/app/main/ui/auth/register.cljs b/frontend/src/app/main/ui/auth/register.cljs
index 84ed3de243..9cb0ed2938 100644
--- a/frontend/src/app/main/ui/auth/register.cljs
+++ b/frontend/src/app/main/ui/auth/register.cljs
@@ -121,15 +121,25 @@
(when (contains? @cf/flags :demo-warning)
[:& demo-warning])
+ (when login/show-alt-login-buttons?
+ [:*
+ [:span.separator
+ [:span.line]
+ [:span.text (tr "labels.continue-with")]
+ [:span.line]]
+
+ [:div.buttons
+ [:& login/login-buttons {:params params}]]
+
+ (when (or (contains? @cf/flags :login)
+ (contains? @cf/flags :login-with-ldap))
+ [:span.separator
+ [:span.line]
+ [:span.text (tr "labels.or")]
+ [:span.line]])])
+
[:& register-form {:params params}]
- (when login/show-alt-login-buttons?
- [:*
- [:span.separator (tr "labels.or")]
-
- [:div.buttons
- [:& login/login-buttons {:params params}]]])
-
[:div.links
[:div.link-entry
[:span (tr "auth.already-have-account") " "]
diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs
index db8a4ead53..d6ff5f0daf 100644
--- a/frontend/src/app/main/ui/icons.cljs
+++ b/frontend/src/app/main/ui/icons.cljs
@@ -170,6 +170,10 @@
(def uppercase (icon-xref :uppercase))
(def user (icon-xref :user))
+(def brand-openid (icon-xref :brand-openid))
+(def brand-github (icon-xref :brand-github))
+(def brand-gitlab (icon-xref :brand-gitlab))
+(def brand-google (icon-xref :brand-google))
(def loader-pencil
(mf/html
diff --git a/frontend/translations/ar.po b/frontend/translations/ar.po
index e042e8d6dd..c1af242ae8 100644
--- a/frontend/translations/ar.po
+++ b/frontend/translations/ar.po
@@ -56,10 +56,6 @@ msgstr "تسجيل الدخول هنا"
msgid "auth.login-submit"
msgstr "تسجيل الدخول"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "أدخل التفاصيل أدناه"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "سعيد برؤيتك مجددا!"
diff --git a/frontend/translations/ca.po b/frontend/translations/ca.po
index af65d41fe1..c960744f7c 100644
--- a/frontend/translations/ca.po
+++ b/frontend/translations/ca.po
@@ -59,10 +59,6 @@ msgstr "Inicieu la sessió aquí"
msgid "auth.login-submit"
msgstr "Entra"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "Introduïu les vostres dades a continuació"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "Ens agrada tornar a veure-vos!"
diff --git a/frontend/translations/da.po b/frontend/translations/da.po
index 16ebcb64d0..9f09c8dbd5 100644
--- a/frontend/translations/da.po
+++ b/frontend/translations/da.po
@@ -59,10 +59,6 @@ msgstr "Log på her"
msgid "auth.login-submit"
msgstr "Log på"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "Indtast dine oplysninger nedenunder"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "Fedt at se dig igen!"
diff --git a/frontend/translations/de.po b/frontend/translations/de.po
index beafaf1319..d2dd6c2b97 100644
--- a/frontend/translations/de.po
+++ b/frontend/translations/de.po
@@ -59,10 +59,6 @@ msgstr "Hier einloggen"
msgid "auth.login-submit"
msgstr "Anmelden"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "Geben Sie unten Ihre Daten ein"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "Schön, Sie wiederzusehen!"
diff --git a/frontend/translations/el.po b/frontend/translations/el.po
index f3100b1c49..582c5da77b 100644
--- a/frontend/translations/el.po
+++ b/frontend/translations/el.po
@@ -54,10 +54,6 @@ msgstr "Συνδεθείτε εδώ"
msgid "auth.login-submit"
msgstr "Συνδεθείτε"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "Εισαγάγετε τα στοιχεία σας παρακάτω"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "Χαίρομαι που σας ξαναδώ"
diff --git a/frontend/translations/en.po b/frontend/translations/en.po
index bcd0367177..6eacdf3a18 100644
--- a/frontend/translations/en.po
+++ b/frontend/translations/en.po
@@ -57,25 +57,21 @@ msgstr "Login here"
msgid "auth.login-submit"
msgstr "Login"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "Enter your details below"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "Great to see you again!"
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-with-github-submit"
-msgstr "Login with GitHub"
+msgstr "GitHub"
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-with-gitlab-submit"
-msgstr "Login with Gitlab"
+msgstr "Gitlab"
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-with-google-submit"
-msgstr "Login with Google"
+msgstr "Google"
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-with-ldap-submit"
@@ -83,7 +79,7 @@ msgstr "Login with LDAP"
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-with-oidc-submit"
-msgstr "Login with OpenID (SSO)"
+msgstr "OpenID Connect"
#: src/app/main/ui/auth/recovery.cljs
msgid "auth.new-password"
@@ -1231,6 +1227,9 @@ msgstr "Old password"
msgid "labels.only-yours"
msgstr "Only yours"
+msgid "labels.continue-with"
+msgstr "Continue with"
+
msgid "labels.or"
msgstr "or"
diff --git a/frontend/translations/es.po b/frontend/translations/es.po
index f557a462b0..9638816428 100644
--- a/frontend/translations/es.po
+++ b/frontend/translations/es.po
@@ -59,25 +59,21 @@ msgstr "Entra aquí"
msgid "auth.login-submit"
msgstr "Entrar"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "Introduce tus datos aquí"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "¡Un placer verte de nuevo!"
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-with-github-submit"
-msgstr "Entrar con Github"
+msgstr "Github"
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-with-gitlab-submit"
-msgstr "Entrar con Gitlab"
+msgstr "Gitlab"
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-with-google-submit"
-msgstr "Entrar con Google"
+msgstr "Google"
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-with-ldap-submit"
@@ -85,7 +81,7 @@ msgstr "Entrar con LDAP"
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-with-oidc-submit"
-msgstr "Entrar con OpenID (SSO)"
+msgstr "OpenID Connect"
#: src/app/main/ui/auth/recovery.cljs
msgid "auth.new-password"
@@ -1232,6 +1228,9 @@ msgstr "Contraseña anterior"
msgid "labels.only-yours"
msgstr "Sólo los tuyos"
+msgid "labels.continue-with"
+msgstr "Continúa con"
+
msgid "labels.or"
msgstr "o"
diff --git a/frontend/translations/fr.po b/frontend/translations/fr.po
index 69fc424656..88c306d11c 100644
--- a/frontend/translations/fr.po
+++ b/frontend/translations/fr.po
@@ -59,10 +59,6 @@ msgstr "Se connecter ici"
msgid "auth.login-submit"
msgstr "Se connecter"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "Entrez vos informations ci‑dessous"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "Ravi de vous revoir !"
diff --git a/frontend/translations/he.po b/frontend/translations/he.po
index 5d3be3b379..2aa0f8b376 100644
--- a/frontend/translations/he.po
+++ b/frontend/translations/he.po
@@ -56,10 +56,6 @@ msgstr "כניסה מכאן"
msgid "auth.login-submit"
msgstr "כניסה"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "נא למלא את הפרטים שלך להלן"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "שמחים לראות אותך שוב!"
diff --git a/frontend/translations/id.po b/frontend/translations/id.po
index f9380e1778..adc798da59 100644
--- a/frontend/translations/id.po
+++ b/frontend/translations/id.po
@@ -63,10 +63,6 @@ msgstr "Masuk disini"
msgid "auth.login-submit"
msgstr "Masuk"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "Masukkan detail anda di bawah ini"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "Senang bertemu denganmu lagi!"
diff --git a/frontend/translations/pt_BR.po b/frontend/translations/pt_BR.po
index cd0442519e..54a936db91 100644
--- a/frontend/translations/pt_BR.po
+++ b/frontend/translations/pt_BR.po
@@ -59,10 +59,6 @@ msgstr "Entrar aqui"
msgid "auth.login-submit"
msgstr "Entrar"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "Insira seus dados abaixo"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "Bom te ver de novo!"
diff --git a/frontend/translations/ro.po b/frontend/translations/ro.po
index 26c1f7e93e..14f72441b3 100644
--- a/frontend/translations/ro.po
+++ b/frontend/translations/ro.po
@@ -60,10 +60,6 @@ msgstr "Conectează-te"
msgid "auth.login-submit"
msgstr "Intră în cont"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "Introduceți detaliile dvs. mai jos"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "Mă bucur să te văd din nou!"
diff --git a/frontend/translations/ru.po b/frontend/translations/ru.po
index c8c118ca54..dac4374a02 100644
--- a/frontend/translations/ru.po
+++ b/frontend/translations/ru.po
@@ -48,10 +48,6 @@ msgstr "Войти здесь"
msgid "auth.login-submit"
msgstr "Вход"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "Введите информацию о себе ниже"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "Рады видеть Вас снова!"
diff --git a/frontend/translations/tr.po b/frontend/translations/tr.po
index bb1120a81b..33a992d25d 100644
--- a/frontend/translations/tr.po
+++ b/frontend/translations/tr.po
@@ -59,10 +59,6 @@ msgstr "Buradan oturum açın"
msgid "auth.login-submit"
msgstr "Oturum aç"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "Bilgilerini aşağıdaki alana gir"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "Seni tekrar görmek süper!"
diff --git a/frontend/translations/zh_CN.po b/frontend/translations/zh_CN.po
index 43cfecabd2..b29c3026b7 100644
--- a/frontend/translations/zh_CN.po
+++ b/frontend/translations/zh_CN.po
@@ -55,10 +55,6 @@ msgstr "在这里登录"
msgid "auth.login-submit"
msgstr "登录"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "请在下面输入你的详细信息"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "很高兴又见到你!"
diff --git a/frontend/translations/zh_Hant.po b/frontend/translations/zh_Hant.po
index dcfd8e9bc4..9627b07e1c 100644
--- a/frontend/translations/zh_Hant.po
+++ b/frontend/translations/zh_Hant.po
@@ -55,10 +55,6 @@ msgstr "在此登入"
msgid "auth.login-submit"
msgstr "登入"
-#: src/app/main/ui/auth/login.cljs
-msgid "auth.login-subtitle"
-msgstr "在下方輸入您的詳細資訊"
-
#: src/app/main/ui/auth/login.cljs
msgid "auth.login-title"
msgstr "很高興再次見到你!"