From b054addb9c15b0c7e964bd956442f32b4981f03c Mon Sep 17 00:00:00 2001 From: Harvey Kandola Date: Mon, 13 May 2019 16:14:11 +0100 Subject: [PATCH] Support dual login via LDAP and forms authentication Closes #256 as we now support dual login -- LDAP and forms authentication. Also bumped the vendored library to LDAP.v3 as it contains bug fixes. --- Gopkg.lock | 10 +- NOTICES.md | 23 +- README.md | 12 +- domain/auth/ldap/endpoint.go | 97 +++++--- domain/auth/ldap/ldap.go | 2 +- gui/.watchmanconfig | 2 +- gui/app/components/customize/auth-settings.js | 1 + gui/app/mixins/auth.js | 16 +- gui/app/pods/auth/login/template.hbs | 4 +- gui/app/pods/customize/notice/template.hbs | 21 +- .../components/customize/auth-settings.hbs | 5 + .../components/customize/user-admin.hbs | 2 +- .../components/customize/user-list.hbs | 4 +- model/auth/ldap.go | 1 + vendor/gopkg.in/ldap.v2/.travis.yml | 31 --- vendor/gopkg.in/ldap.v2/Makefile | 52 ---- vendor/gopkg.in/ldap.v2/atomic_value.go | 13 - vendor/gopkg.in/ldap.v2/atomic_value_go13.go | 28 --- vendor/gopkg.in/ldap.v2/error.go | 155 ------------ .../gopkg.in/{ldap.v2 => ldap.v3}/.gitignore | 0 vendor/gopkg.in/ldap.v3/.travis.yml | 31 +++ vendor/gopkg.in/ldap.v3/CONTRIBUTING.md | 12 + vendor/gopkg.in/{ldap.v2 => ldap.v3}/LICENSE | 0 vendor/gopkg.in/ldap.v3/Makefile | 82 ++++++ .../gopkg.in/{ldap.v2 => ldap.v3}/README.md | 7 +- vendor/gopkg.in/{ldap.v2 => ldap.v3}/add.go | 16 +- vendor/gopkg.in/{ldap.v2 => ldap.v3}/bind.go | 108 ++++---- .../gopkg.in/{ldap.v2 => ldap.v3}/client.go | 1 + .../gopkg.in/{ldap.v2 => ldap.v3}/compare.go | 20 +- vendor/gopkg.in/{ldap.v2 => ldap.v3}/conn.go | 98 ++++++-- .../gopkg.in/{ldap.v2 => ldap.v3}/control.go | 123 +++++++-- vendor/gopkg.in/{ldap.v2 => ldap.v3}/debug.go | 0 vendor/gopkg.in/{ldap.v2 => ldap.v3}/del.go | 8 +- vendor/gopkg.in/{ldap.v2 => ldap.v3}/dn.go | 30 +-- vendor/gopkg.in/{ldap.v2 => ldap.v3}/doc.go | 0 vendor/gopkg.in/ldap.v3/error.go | 234 ++++++++++++++++++ .../gopkg.in/{ldap.v2 => ldap.v3}/filter.go | 4 - vendor/gopkg.in/{ldap.v2 => ldap.v3}/ldap.go | 86 ++++--- vendor/gopkg.in/ldap.v3/moddn.go | 104 ++++++++ .../gopkg.in/{ldap.v2 => ldap.v3}/modify.go | 77 +++--- .../{ldap.v2 => ldap.v3}/passwdmodify.go | 17 +- .../gopkg.in/{ldap.v2 => ldap.v3}/search.go | 22 +- 42 files changed, 977 insertions(+), 582 deletions(-) delete mode 100644 vendor/gopkg.in/ldap.v2/.travis.yml delete mode 100644 vendor/gopkg.in/ldap.v2/Makefile delete mode 100644 vendor/gopkg.in/ldap.v2/atomic_value.go delete mode 100644 vendor/gopkg.in/ldap.v2/atomic_value_go13.go delete mode 100644 vendor/gopkg.in/ldap.v2/error.go rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/.gitignore (100%) create mode 100644 vendor/gopkg.in/ldap.v3/.travis.yml create mode 100644 vendor/gopkg.in/ldap.v3/CONTRIBUTING.md rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/LICENSE (100%) create mode 100644 vendor/gopkg.in/ldap.v3/Makefile rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/README.md (85%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/add.go (90%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/bind.go (54%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/client.go (93%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/compare.go (80%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/conn.go (86%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/control.go (75%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/debug.go (100%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/del.go (91%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/dn.go (91%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/doc.go (100%) create mode 100644 vendor/gopkg.in/ldap.v3/error.go rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/filter.go (98%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/ldap.go (82%) create mode 100644 vendor/gopkg.in/ldap.v3/moddn.go rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/modify.go (63%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/passwdmodify.go (92%) rename vendor/gopkg.in/{ldap.v2 => ldap.v3}/search.go (96%) diff --git a/Gopkg.lock b/Gopkg.lock index 6a5c3072..4d7cf55f 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -299,12 +299,12 @@ version = "v1.2" [[projects]] - digest = "1:93aaeb913621a3a53aaa78592c00f46d63e3bb0ea76e2d9b07327b50959a5778" - name = "gopkg.in/ldap.v2" + digest = "1:e9a0fa7c2dfc90e0fae16be5825ad98074d8704f5fcebfdc289a8e8fb0f8e4b5" + name = "gopkg.in/ldap.v3" packages = ["."] pruneopts = "UT" - revision = "bb7a9ca6e4fbc2129e3db588a34bc970ffe811a9" - version = "v2.5.1" + revision = "9f0d712775a0973b7824a1585a86a4ea1d5263d9" + version = "v3.0.3" [solve-meta] analyzer-name = "dep" @@ -334,7 +334,7 @@ "golang.org/x/oauth2", "gopkg.in/alexcesaro/quotedprintable.v3", "gopkg.in/andygrunwald/go-jira.v1", - "gopkg.in/ldap.v2", + "gopkg.in/ldap.v3", ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/NOTICES.md b/NOTICES.md index 1f662ec8..744d021c 100644 --- a/NOTICES.md +++ b/NOTICES.md @@ -36,7 +36,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. https://github.com/gorilla/context https://github.com/gorilla/mux - + Copyright (c) 2012 Rodrigo Moraes. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -197,7 +197,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI https://github.com/jmoiron/sqlx - + Copyright (c) 2013, Jason Moiron Permission is hereby granted, free of charge, to any person @@ -223,7 +223,7 @@ OTHER DEALINGS IN THE SOFTWARE. https://github.com/mytrile/mime-ext - + The MIT License (MIT) Copyright (c) 2014 Dimitar Kostov @@ -247,7 +247,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. https://github.com/nu7hatch/gouuid - + Copyright (C) 2011 by Krzysztof Kowalik Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -270,7 +270,7 @@ SOFTWARE. https://github.com/rs/xid - + Copyright (c) 2015 Olivier Poitrey Permission is hereby granted, free of charge, to any person obtaining a copy @@ -318,7 +318,7 @@ SOFTWARE. https://github.com/jordan-wright/email - + Elements of the software in this file were modified from github.com/jordan-wright/email and are subject to the licence below: @@ -625,9 +625,9 @@ OTHER DEALINGS IN THE SOFTWARE. https://github.com/blueimp/JavaScript-MD5 JavaScript MD5 1.0.1 - + Copyright 2011, Sebastian Tschan -https://blueimp.net +https://blueimp.net Licensed under the MIT license: http://www.opensource.org/licenses/MIT @@ -834,7 +834,7 @@ such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. - + 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an @@ -1471,7 +1471,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -https://github.com/arasatasaygin/is.js +https://github.com/arasatasaygin/is.js The MIT License (MIT) @@ -1853,6 +1853,7 @@ SOFTWARE. gopkg.in/ldap.v2 +gopkg.in/ldap.v3 The MIT License (MIT) @@ -2568,4 +2569,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +THE SOFTWARE. diff --git a/README.md b/README.md index d2c7994e..adc9f928 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,13 @@ All you need to provide is PostgreSQL, Microsoft SQL Server or any MySQL variant ## Latest Release -[Community Edition: v2.4.2](https://github.com/documize/community/releases) +[Community Edition: v2.5.0](https://github.com/documize/community/releases) -[Enterprise Edition: v2.4.2](https://www.documize.com/downloads) +[Enterprise Edition: v2.5.0](https://www.documize.com/downloads) > *We provide frequent product updates for both cloud and self-hosted customers.* > -> **Harvey Kandola, CEO/Founder, Documize, Inc.** +> **Harvey Kandola, CEO/Founder @ Documize** ## OS support @@ -45,17 +45,19 @@ Heck, Documize will probably run just fine on a Raspberry Pi. - Brave - Vivaldi - Opera -- Edge (v42+) +- Microsoft Edge (v42+) ## Technology Stack -- Go (v1.12.4) +- Go (v1.12.5) - Ember JS (v3.8.0) ## Authentication Options Besides email/password login, you can also connect to LDAP/Active Directory or Red Hat Keycloak server. +Dual authentication of LDAP and email/password is also supported. + ## The Legal Bit diff --git a/domain/auth/ldap/endpoint.go b/domain/auth/ldap/endpoint.go index b7200715..bdbbb7fa 100644 --- a/domain/auth/ldap/endpoint.go +++ b/domain/auth/ldap/endpoint.go @@ -278,8 +278,6 @@ func (h *Handler) Authenticate(w http.ResponseWriter, r *http.Request) { dom = h.Store.Organization.CheckDomain(ctx, dom) // TODO optimize by removing this once js allows empty domains - h.Runtime.Log.Info("LDAP login request " + username + " @ " + dom) - // Get the org and it's associated LDAP config. org, err := h.Store.Organization.GetOrganizationByDomain(dom) if err != nil { @@ -298,6 +296,13 @@ func (h *Handler) Authenticate(w http.ResponseWriter, r *http.Request) { ctx.OrgID = org.RefID + // We first connect to LDAP and try to authenticate user. + // If user auth fails and dual authentication is enabled, + // we try to authenticate with email/password combo. + var u user.User + + // Try LDAP + h.Runtime.Log.Info("LDAP login request " + username + " @ " + dom) l, err := connect(lc) if err != nil { response.WriteBadRequestError(w, method, "unable to dial LDAP server") @@ -305,49 +310,83 @@ func (h *Handler) Authenticate(w http.ResponseWriter, r *http.Request) { return } defer l.Close() - lu, ok, err := authenticate(l, lc, username, password) if err != nil { response.WriteBadRequestError(w, method, "error during LDAP authentication") h.Runtime.Log.Error(method, err) return } - if !ok { - h.Runtime.Log.Info("LDAP failed login request for " + username + " @ " + dom) - response.WriteUnauthorizedError(w) - return - } - h.Runtime.Log.Info("LDAP logon completed " + lu.Email) + // If OK then we complete LDAP specific processing + if ok { + h.Runtime.Log.Info("LDAP logon completed " + lu.Email) - u, err := h.Store.User.GetByDomain(ctx, dom, lu.Email) - if err != nil && err != sql.ErrNoRows { - response.WriteServerError(w, method, err) - h.Runtime.Log.Error(method, err) - return - } - - // Create user account if not found - if err == sql.ErrNoRows { - h.Runtime.Log.Info("Adding new LDAP user " + lu.Email + " @ " + dom) - - u = convertUser(lc, lu) - u.Salt = secrets.GenerateSalt() - u.Password = secrets.GeneratePassword(secrets.GenerateRandomPassword(), u.Salt) - - u, err = auth.AddExternalUser(ctx, h.Runtime, h.Store, u, lc.DefaultPermissionAddSpace) - if err != nil { + u, err = h.Store.User.GetByDomain(ctx, dom, lu.Email) + if err != nil && err != sql.ErrNoRows { response.WriteServerError(w, method, err) h.Runtime.Log.Error(method, err) return } + + // Create user account if not found + if err == sql.ErrNoRows { + h.Runtime.Log.Info("Adding new LDAP user " + lu.Email + " @ " + dom) + + u = convertUser(lc, lu) + u.Salt = secrets.GenerateSalt() + u.Password = secrets.GeneratePassword(secrets.GenerateRandomPassword(), u.Salt) + + u, err = auth.AddExternalUser(ctx, h.Runtime, h.Store, u, lc.DefaultPermissionAddSpace) + if err != nil { + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + } } + // If LDAP authentication failed, we check to see if we are allowed + // to perform authentication via regular email/password. + if !ok { + // Return as unauthorized if dual authentication not enabled. + if !lc.AllowFormsAuth { + h.Runtime.Log.Info("LDAP failed login request for " + username + " @ " + dom) + response.WriteUnauthorizedError(w) + return + } + + h.Runtime.Log.Info("Trying forms auth as LDAP login login failed for " + username + " @ " + dom) + + // Now try regular email/password authentication. + u, err = h.Store.User.GetByDomain(ctx, dom, username) + if err == sql.ErrNoRows { + response.WriteUnauthorizedError(w) + return + } + if err != nil && err != sql.ErrNoRows { + h.Runtime.Log.Error("unable to fetch user", err) + response.WriteServerError(w, method, err) + return + } + if len(u.Reset) > 0 || len(u.Password) == 0 { + response.WriteUnauthorizedError(w) + return + } + + // Password correct and active user + if username != strings.TrimSpace(strings.ToLower(u.Email)) || !secrets.MatchPassword(u.Password, password, u.Salt) { + response.WriteUnauthorizedError(w) + return + } + } + + // Below is standard flow for user authentication regardless + // if they used LDAP or email/password combo. + // Attach user accounts and work out permissions. usr.AttachUserAccounts(ctx, *h.Store, org.RefID, &u) - // No accounts signals data integrity problem - // so we reject login request. + // No accounts signals data integrity problem so we reject login request. if len(u.Accounts) == 0 { response.WriteUnauthorizedError(w) h.Runtime.Log.Error(method, err) @@ -366,7 +405,7 @@ func (h *Handler) Authenticate(w http.ResponseWriter, r *http.Request) { } } - // Generate JWT token + // Send back newly generated JWT token. authModel := ath.AuthenticationModel{} authModel.Token = auth.GenerateJWT(h.Runtime, u.RefID, org.RefID, dom) authModel.User = u diff --git a/domain/auth/ldap/ldap.go b/domain/auth/ldap/ldap.go index 11c53c1e..d0e62f61 100644 --- a/domain/auth/ldap/ldap.go +++ b/domain/auth/ldap/ldap.go @@ -21,7 +21,7 @@ import ( lm "github.com/documize/community/model/auth" "github.com/documize/community/model/user" "github.com/pkg/errors" - ld "gopkg.in/ldap.v2" + ld "gopkg.in/ldap.v3" ) // Connect establishes connection to LDAP server. diff --git a/gui/.watchmanconfig b/gui/.watchmanconfig index 651cbd07..e23b9c2d 100644 --- a/gui/.watchmanconfig +++ b/gui/.watchmanconfig @@ -1,3 +1,3 @@ { - "ignore_dirs": ["tmp", "dist", "dist-prod"] + "ignore_dirs": ["tmp", "dist", "dist-prod", "tests", "node_modules"] } diff --git a/gui/app/components/customize/auth-settings.js b/gui/app/components/customize/auth-settings.js index 07d780f8..4bd89985 100644 --- a/gui/app/components/customize/auth-settings.js +++ b/gui/app/components/customize/auth-settings.js @@ -113,6 +113,7 @@ export default Component.extend(ModalMixin, Notifier, { ldapConfig = JSON.parse(ldapConfig); ldapConfig.defaultPermissionAddSpace = ldapConfig.hasOwnProperty('defaultPermissionAddSpace') ? ldapConfig.defaultPermissionAddSpace : false; ldapConfig.disableLogout = ldapConfig.hasOwnProperty('disableLogout') ? ldapConfig.disableLogout : true; + ldapConfig.allowFormsAuth = ldapConfig.hasOwnProperty('allowFormsAuth') ? ldapConfig.allowFormsAuth : false; } this.set('ldapConfig', ldapConfig); diff --git a/gui/app/mixins/auth.js b/gui/app/mixins/auth.js index 13c757d8..52123bc2 100644 --- a/gui/app/mixins/auth.js +++ b/gui/app/mixins/auth.js @@ -15,8 +15,9 @@ import Mixin from '@ember/object/mixin'; export default Mixin.create({ appMeta: service(), isAuthProviderDocumize: true, - IsAuthProviderKeycloak: false, - IsAuthProviderLDAP: false, + isAuthProviderKeycloak: false, + isAuthProviderLDAP: false, + isDualAuth: false, init() { this._super(...arguments); @@ -25,5 +26,16 @@ export default Mixin.create({ this.set('isAuthProviderDocumize', this.get('appMeta.authProvider') === constants.AuthProvider.Documize); this.set('isAuthProviderKeycloak', this.get('appMeta.authProvider') === constants.AuthProvider.Keycloak); this.set('isAuthProviderLDAP', this.get('appMeta.authProvider') === constants.AuthProvider.LDAP); + + if (this.get('appMeta.authProvider') === constants.AuthProvider.LDAP) { + let config = this.get('appMeta.authConfig'); + + if (!_.isUndefined(config) && !_.isNull(config) && !_.isEmpty(config) ) { + config = JSON.parse(config); + this.set('isDualAuth', config.allowFormsAuth); + } else { + this.set('isDualAuth', false); + } + } } }); diff --git a/gui/app/pods/auth/login/template.hbs b/gui/app/pods/auth/login/template.hbs index 8f85c4ae..43a99528 100644 --- a/gui/app/pods/auth/login/template.hbs +++ b/gui/app/pods/auth/login/template.hbs @@ -13,7 +13,7 @@ {{focus-input type="email" value=email id="authEmail" class="form-control mousetrap" placeholder="" autocomplete="username email"}} {{/if}} {{#if isAuthProviderLDAP}} - + {{focus-input type="text" value=username id="authUsername" class="form-control mousetrap" placeholder="network domain username" autocomplete="username"}} {{/if}} @@ -23,7 +23,7 @@ {{input type="password" value=password id="authPassword" class="form-control" autocomplete="current-password"}} {{/if}} {{#if isAuthProviderLDAP}} - + {{input type="password" value=password id="authPassword" class="form-control" autocomplete="current-password"}} {{/if}} diff --git a/gui/app/pods/customize/notice/template.hbs b/gui/app/pods/customize/notice/template.hbs index 71ddd27b..d5bd2c5e 100644 --- a/gui/app/pods/customize/notice/template.hbs +++ b/gui/app/pods/customize/notice/template.hbs @@ -38,7 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. https://github.com/gorilla/context https://github.com/gorilla/mux - + Copyright (c) 2012 Rodrigo Moraes. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -199,7 +199,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI https://github.com/jmoiron/sqlx - + Copyright (c) 2013, Jason Moiron Permission is hereby granted, free of charge, to any person @@ -225,7 +225,7 @@ OTHER DEALINGS IN THE SOFTWARE. https://github.com/mytrile/mime-ext - + The MIT License (MIT) Copyright (c) 2014 Dimitar Kostov @@ -249,7 +249,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. https://github.com/nu7hatch/gouuid - + Copyright (C) 2011 by Krzysztof Kowalik chris@nu7hat.ch Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -272,7 +272,7 @@ SOFTWARE. https://github.com/rs/xid - + Copyright (c) 2015 Olivier Poitrey rs@dailymotion.com Permission is hereby granted, free of charge, to any person obtaining a copy @@ -320,7 +320,7 @@ SOFTWARE. https://github.com/jordan-wright/email - + Elements of the software in this file were modified from github.com/jordan-wright/email and are subject to the licence below: @@ -627,9 +627,9 @@ OTHER DEALINGS IN THE SOFTWARE. https://github.com/blueimp/JavaScript-MD5 JavaScript MD5 1.0.1 - + Copyright 2011, Sebastian Tschan -https://blueimp.net +https://blueimp.net Licensed under the MIT license: http://www.opensource.org/licenses/MIT @@ -836,7 +836,7 @@ such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. - + 1. You may copy and distribute verbatim copies of the Librarys complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an @@ -1473,7 +1473,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -https://github.com/arasatasaygin/is.js +https://github.com/arasatasaygin/is.js The MIT License (MIT) @@ -1855,6 +1855,7 @@ SOFTWARE. gopkg.in/ldap.v2 +gopkg.in/ldap.v3 The MIT License (MIT) diff --git a/gui/app/templates/components/customize/auth-settings.hbs b/gui/app/templates/components/customize/auth-settings.hbs index e6c64eec..15c047d7 100644 --- a/gui/app/templates/components/customize/auth-settings.hbs +++ b/gui/app/templates/components/customize/auth-settings.hbs @@ -159,6 +159,11 @@ {{x-toggle value=ldapConfig.defaultPermissionAddSpace size="medium" theme="light" onToggle=(action (mut ldapConfig.defaultPermissionAddSpace))}} +
+ + {{x-toggle value=ldapConfig.allowFormsAuth size="medium" theme="light" onToggle=(action (mut ldapConfig.allowFormsAuth))}} + Enable login via LDAP and regular Documize email/password (useful for testing LDAP) +
{{ui/ui-button color=constants.Color.Yellow light=true label="Test →" onClick=(action "onLDAPPreview")}} {{ui/ui-button-gap}} {{/if}} diff --git a/gui/app/templates/components/customize/user-admin.hbs b/gui/app/templates/components/customize/user-admin.hbs index d7ba39a7..36438d18 100644 --- a/gui/app/templates/components/customize/user-admin.hbs +++ b/gui/app/templates/components/customize/user-admin.hbs @@ -1,5 +1,5 @@
- {{#if isAuthProviderDocumize}} + {{#if (or isAuthProviderDocumize IsDualAuth)}} {{ui/ui-button color=constants.Color.Green light=true icon=constants.Icon.Person label=constants.Label.Add onClick=(action "onOpenUserModal")}}