From 945fadaf009fb3546d486763cf1eb00e9b0d67b8 Mon Sep 17 00:00:00 2001 From: Harvey Kandola Date: Fri, 17 Mar 2017 19:01:32 +0000 Subject: [PATCH] sync users wip --- app/app/components/auth-settings.js | 22 ++-- app/app/pods/auth/keycloak/route.js | 31 +++-- app/app/pods/auth/keycloak/template.hbs | 16 ++- app/app/pods/auth/login/route.js | 8 +- .../templates/components/auth-settings.hbs | 12 +- core/api/endpoint/keycloak.go | 107 +++++++++++++++++- 6 files changed, 166 insertions(+), 30 deletions(-) diff --git a/app/app/components/auth-settings.js b/app/app/components/auth-settings.js index 2f815dd4..d2c8b66a 100644 --- a/app/app/components/auth-settings.js +++ b/app/app/components/auth-settings.js @@ -25,11 +25,15 @@ export default Ember.Component.extend({ KeycloakRealmError: computed.empty('keycloakConfig.realm'), KeycloakClientIdError: computed.empty('keycloakConfig.clientId'), KeycloakPublicKeyError: computed.empty('keycloakConfig.publicKey'), + KeycloakAdminUserError: computed.empty('keycloakConfig.adminUser'), + KeycloakAdminPasswordError: computed.empty('keycloakConfig.adminPassword'), keycloakConfig: { url: '', realm: '', clientId: '', publicKey: '', + adminUser: '', + adminPassword: '' }, didReceiveAttrs() { @@ -89,16 +93,14 @@ export default Ember.Component.extend({ this.$("#keycloak-publicKey").focus(); return; } - - // let pk = this.get('keycloakConfig.publicKey'); - // if (is.not.startWith(pk, '-----BEGIN PUBLIC KEY-----')) { - // pk = '-----BEGIN PUBLIC KEY-----' + pk; - // } - // if (is.not.endWith(pk, '-----END PUBLIC KEY-----')) { - // pk = pk + '-----END PUBLIC KEY-----' ; - // } - - // this.set('keycloakConfig.publicKey', pk); + if (this.get('KeycloakAdminUserError')) { + this.$("#keycloak-admin-user").focus(); + return; + } + if (this.get('KeycloakAdminPasswordError')) { + this.$("#keycloak-admin-password").focus(); + return; + } config = Ember.copy(this.get('keycloakConfig')); Ember.set(config, 'publicKey', encoding.Base64.encode(this.get('keycloakConfig.publicKey'))); diff --git a/app/app/pods/auth/keycloak/route.js b/app/app/pods/auth/keycloak/route.js index f1445dd1..05c3f995 100644 --- a/app/app/pods/auth/keycloak/route.js +++ b/app/app/pods/auth/keycloak/route.js @@ -15,7 +15,8 @@ import constants from '../../../utils/constants'; export default Ember.Route.extend({ session: Ember.inject.service(), appMeta: Ember.inject.service(), - kcAuth: Ember.inject.service(), + kcAuth: Ember.inject.service(), + localStorage: Ember.inject.service(), queryParams: { mode: { refreshModel: false @@ -33,11 +34,16 @@ export default Ember.Route.extend({ return; } + if (this.get('mode') === 'reject') { + return; + } + this.get('kcAuth').boot(JSON.parse(authConfig)).then((kc) => { if (!kc.authenticated) { this.get('kcAuth').login().then(() => { }, (reject) => { - console.log(reject); + this.get('localStorage').storeSessionItem('kc-error', reject); + this.transitionTo('auth.keycloak', { queryParams: { mode: 'reject' }}); }); } @@ -47,16 +53,23 @@ export default Ember.Route.extend({ this.get('audit').record("logged-in-keycloak"); this.transitionTo('folders'); }, (reject) => { - debugger; - console.log(">>>>> Documize Keycloak authentication failure"); - this.transitionTo('auth.login'); + this.get('localStorage').storeSessionItem('kc-error', reject); + this.transitionTo('auth.keycloak', { queryParams: { mode: 'reject' }}); }); - }, (err) => { - console.log(err); + }, (reject) => { + this.get('localStorage').storeSessionItem('kc-error', reject); + this.transitionTo('auth.keycloak', { queryParams: { mode: 'reject' }}); }); - }, (reason) => { - console.log(reason); + }, (reject) => { + this.get('localStorage').storeSessionItem('kc-error', reject); + this.transitionTo('auth.keycloak', { queryParams: { mode: 'reject' }}); }); }, + + model() { + return { + mode: this.get('mode') + } + } }); diff --git a/app/app/pods/auth/keycloak/template.hbs b/app/app/pods/auth/keycloak/template.hbs index 9f545853..c62a27ca 100644 --- a/app/app/pods/auth/keycloak/template.hbs +++ b/app/app/pods/auth/keycloak/template.hbs @@ -1,4 +1,12 @@ -
-

Authenticating with Keycloak...

- -
+{{#if (is-equal model.mode 'login')}} +
+

Authenticating with Keycloak...

+ +
+{{/if}} + +{{#if (is-equal model.mode 'reject')}} +
+

Keycloak Authentication Failure

+
+{{/if}} diff --git a/app/app/pods/auth/login/route.js b/app/app/pods/auth/login/route.js index 53a14bcc..73a165e8 100644 --- a/app/app/pods/auth/login/route.js +++ b/app/app/pods/auth/login/route.js @@ -15,6 +15,7 @@ import constants from '../../../utils/constants'; export default Ember.Route.extend({ appMeta: Ember.inject.service(), kcAuth: Ember.inject.service(), + localStorage: Ember.inject.service(), showLogin: false, beforeModel(/*transition*/) { @@ -28,13 +29,16 @@ export default Ember.Route.extend({ this.get('kcAuth').boot(JSON.parse(authConfig)).then(() => { this.get('kcAuth').login().then(() => { }, (reject) => { - console.log(reject); + this.get('localStorage').storeSessionItem('kc-error', reject); + this.transitionTo('auth.keycloak', { queryParams: { mode: 'reject' }}); }); }, (reject) => { - console.log(reject); + this.get('localStorage').storeSessionItem('kc-error', reject); + this.transitionTo('auth.keycloak', { queryParams: { mode: 'reject' }}); }); break; + default: this.set('showLogin', true); break; diff --git a/app/app/templates/components/auth-settings.hbs b/app/app/templates/components/auth-settings.hbs index 6c46eb4c..dfca4b7c 100644 --- a/app/app/templates/components/auth-settings.hbs +++ b/app/app/templates/components/auth-settings.hbs @@ -13,7 +13,7 @@ {{#if isKeycloakProvider}}
Keycloak Configuration
-
Connection parameters
+
Connection parameters — create a documize user in Master realm with 'manage-users' role against target realm
@@ -35,6 +35,16 @@
Copy the RSA public key from Realm Settings → Keys
{{textarea id="keycloak-publicKey" type="text" value=keycloakConfig.publicKey rows=7 class=(if KeycloakPublicKeyError 'error')}}
+
+ +
Used to connect with Keycloak and sync users with Documize
+ {{input id="keycloak-admin-user" type="text" value=keycloakConfig.adminUser class=(if KeycloakAdminUserError 'error')}} +
+
+ +
Used to connect with Keycloak and sync users with Documize
+ {{input id="keycloak-admin-password" type="password" value=keycloakConfig.adminPassword class=(if KeycloakAdminPasswordError 'error')}} +
{{/if}}
save
diff --git a/core/api/endpoint/keycloak.go b/core/api/endpoint/keycloak.go index b8d26602..bfdbc359 100644 --- a/core/api/endpoint/keycloak.go +++ b/core/api/endpoint/keycloak.go @@ -17,14 +17,18 @@ import ( "fmt" "io/ioutil" "net/http" + "net/url" "strings" + "bytes" + "errors" "github.com/documize/community/core/api/endpoint/models" "github.com/documize/community/core/api/entity" "github.com/documize/community/core/api/request" "github.com/documize/community/core/api/util" "github.com/documize/community/core/log" "github.com/documize/community/core/utility" + "strconv" ) // AuthenticateKeycloak checks Keycloak authentication credentials. @@ -158,6 +162,11 @@ func AuthenticateKeycloak(w http.ResponseWriter, r *http.Request) { return } + err = SyncUsers(ac) + if err != nil { + log.Error("su", err) + } + writeSuccessBytes(w, json) } @@ -240,6 +249,79 @@ func addUser(p request.Persister, a keycloakAuthRequest) (u entity.User, err err return p.GetUser(userID) } +// SyncUsers gets list of Keycloak users for specified Realm, Client Id +func SyncUsers(c keycloakConfig) (err error) { + form := url.Values{} + form.Add("username", c.AdminUser) + form.Add("password", c.AdminPassword) + form.Add("client_id", "admin-cli") + form.Add("grant_type", "password") + + req, err := http.NewRequest("POST", + fmt.Sprintf("%s/realms/master/protocol/openid-connect/token", c.URL), + bytes.NewBufferString(form.Encode())) + + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode()))) + + client := &http.Client{} + res, err := client.Do(req) + if err != nil { + return err + } + + defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return err + } + + ka := keycloakAPIAuth{} + err = json.Unmarshal(body, &ka) + if err != nil { + return err + } + + if res.StatusCode != http.StatusOK { + return errors.New("Keycloak authentication failed " + res.Status) + } + + req, err = http.NewRequest("GET", + fmt.Sprintf("%s/admin/realms/%s/users?max=500", c.URL, c.Realm), + nil) + + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", ka.AccessToken)) + + client = &http.Client{} + res, err = client.Do(req) + if err != nil { + return err + } + + defer res.Body.Close() + body, err = ioutil.ReadAll(res.Body) + if err != nil { + return err + } + + u := []keycloakUser{} + err = json.Unmarshal(body, &u) + if err != nil { + return err + } + + if res.StatusCode != http.StatusOK { + return errors.New("Keycloak /users call failed " + res.Status) + } + + log.Info(fmt.Sprintf("%d", res.StatusCode)) + + fmt.Println(fmt.Sprintf("%d len", len(u))) + fmt.Println(u[0].Email) + + return nil +} + // Data received via Keycloak client library type keycloakAuthRequest struct { Domain string `json:"domain"` @@ -254,8 +336,25 @@ type keycloakAuthRequest struct { // Keycloak server configuration type keycloakConfig struct { - URL string `json:"url"` - Realm string `json:"realm"` - ClientID string `json:"clientId"` - PublicKey string `json:"publicKey"` + URL string `json:"url"` + Realm string `json:"realm"` + ClientID string `json:"clientId"` + PublicKey string `json:"publicKey"` + AdminUser string `json:"adminUser"` + AdminPassword string `json:"adminPassword"` +} + +// keycloakAPIAuth is returned when authenticating with Keycloak REST API. +type keycloakAPIAuth struct { + AccessToken string `json:"access_token"` +} + +// keycloakUser details user record returned by Keycloak +type keycloakUser struct { + ID string `json:"id"` + Username string `json:"username"` + Email string `json:"email"` + Firstname string `json:"firstName"` + Lastname string `json:"lastName"` + Enabled bool `json:"enabled"` }