1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-08-02 20:15:26 +02:00

Merge pull request #306 from dereknex/auth-with-cas

Authentication with Central Authentication Service (CAS) such as https://www.apereo.org/projects/cas
This commit is contained in:
Harvey Kandola 2019-08-27 11:05:18 +01:00 committed by GitHub
commit 29d7307537
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
156 changed files with 43417 additions and 23003 deletions

View file

@ -0,0 +1,48 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import { isPresent } from '@ember/utils';
import { reject, resolve } from 'rsvp';
import { inject as service } from '@ember/service';
import Base from 'ember-simple-auth/authenticators/base';
import netUtil from "../utils/net";
export default Base.extend({
ajax: service(),
appMeta: service(),
localStorage: service(),
restore(data) {
// TODO: verify authentication data
if (data) {
return resolve(data);
}
return reject();
},
authenticate(data){
data.domain = netUtil.getSubdomain();
if (!isPresent(data.ticket)) {
return reject("data.ticket is empty");
}
return this.get('ajax').post('public/authenticate/cas', {
data: JSON.stringify(data),
contentType: 'json'
});
},
invalidate() {
this.get('localStorage').clearAll();
return resolve();
}
});

View file

@ -33,6 +33,9 @@ export default Component.extend(ModalMixin, Notifier, {
isLDAPProvider: computed('authProvider', function() {
return this.get('authProvider') === this.get('constants').AuthProvider.LDAP;
}),
isCASProvider: computed('authProvider', function(){
return this.get('authProvider') === this.get('constants').AuthProvider.CAS;
}),
KeycloakUrlError: empty('keycloakConfig.url'),
KeycloakRealmError: empty('keycloakConfig.realm'),
@ -61,6 +64,10 @@ export default Component.extend(ModalMixin, Notifier, {
ldapPreview: null,
ldapConfig: null,
casErrorUrl: empty('casConfig.url'),
casErrorRedirectUrl: empty('casConfig.redirectUrl'),
casConfig:null,
init() {
this._super(...arguments);
@ -122,6 +129,19 @@ export default Component.extend(ModalMixin, Notifier, {
this.set('ldapConfig', ldapConfig);
break;
}
case constants.AuthProvider.CAS: {
let casConfig = this.get('authConfig');
if (_.isUndefined(casConfig) || _.isNull(casConfig) || _.isEmpty(casConfig) ) {
casConfig = {};
} else {
casConfig = JSON.parse(casConfig);
casConfig.url = casConfig.hasOwnProperty('url') ? casConfig.url : '';
casConfig.redirectUrl = casConfig.hasOwnProperty('redirectUrl') ? casConfig.redirectUrl : '';
}
this.set('casConfig', casConfig);
break;
}
}
},
@ -140,6 +160,10 @@ export default Component.extend(ModalMixin, Notifier, {
let constants = this.get('constants');
this.set('authProvider', constants.AuthProvider.LDAP);
},
onCAS() {
let constants = this.get('constants');
this.set('authProvider', constants.AuthProvider.CAS);
},
onLDAPEncryption(e) {
this.set('ldapConfig.encryptionType', e);
@ -231,6 +255,21 @@ export default Component.extend(ModalMixin, Notifier, {
return;
}
break;
case constants.AuthProvider.CAS:
if (this.get('casErrorUrl')) {
$("#cas-url").focus();
return;
}
if (this.get('casErrorRedirectUrl')) {
$("#cas-redirect-url").focus();
return;
}
config = copy(this.get('casConfig'));
config.url = config.url.trim();
config.redirectUrl = config.redirectUrl.trim();
break;
}

View file

@ -25,6 +25,7 @@ let constants = EmberObject.extend({
Documize: 'documize',
Keycloak: 'keycloak',
LDAP: 'ldap',
CAS: 'cas',
ServerTypeLDAP: 'ldap',
ServerTypeAD: 'ad',
EncryptionTypeNone: 'none',

View file

@ -17,6 +17,7 @@ export default Mixin.create({
isAuthProviderDocumize: true,
isAuthProviderKeycloak: false,
isAuthProviderLDAP: false,
isAuthProviderCAS: false,
isDualAuth: false,
init() {
@ -26,6 +27,7 @@ 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);
this.set('isAuthProviderCAS', this.get('appMeta.authProvider') == constants.AuthProvider.CAS);
if (this.get('appMeta.authProvider') === constants.AuthProvider.LDAP) {
let config = this.get('appMeta.authConfig');

View file

@ -0,0 +1,14 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import Controller from '@ember/controller';
export default Controller.extend({});

View file

@ -0,0 +1,65 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import { Promise as EmberPromise } from 'rsvp';
import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
export default Route.extend({
ajax: service(),
session: service(),
appMeta: service(),
localStorage: service(),
queryParams: {
mode: {
refreshModel: true
},
ticket : {
refreshModel: true
}
},
message: '',
mode: 'login',
afterModel(model) {
return new EmberPromise((resolve) => {
let constants = this.get('constants');
if (this.get('appMeta.authProvider') !== constants.AuthProvider.CAS) {
resolve();
}
let ticket = model.ticket;
if (ticket === '') {
resolve();
}
let data = {ticket: ticket};
this.get("session").authenticate('authenticator:cas', data).then(() => {
this.transitionTo('folders');
}, (reject) => {
if (!_.isUndefined(reject.Error)) {
model.message = reject.Error;
} else {
model.message = reject.Error;
}
model.mode = 'reject';
resolve();
});
})
},
model(params) {
return {
mode: this.get('mode'),
message: this.get('message'),
ticket: params.ticket
}
}
});

View file

@ -0,0 +1,12 @@
{{#if (is-equal model.mode "login")}}
<div class="sso-box">
<p>Authenticating with CAS...</p>
<img src="/assets/img/busy-gray.gif">
</div>
{{/if}}
{{#if (is-equal model.mode "reject")}}
<div class="sso-box">
<p>CAS authentication failure</p>
</div>
{{/if}}

View file

@ -16,6 +16,7 @@ import Controller from '@ember/controller';
export default Controller.extend(AuthProvider, {
appMeta: service('app-meta'),
session: service('session'),
invalidCredentials: false,
reset() {
@ -26,7 +27,7 @@ export default Controller.extend(AuthProvider, {
});
}
if (this.get('isAuthProviderLDAP')) {
if (this.get('isAuthProviderLDAP') || this.get('isAuthProviderCAS')) {
this.setProperties({
username: '',
password: ''
@ -62,6 +63,16 @@ export default Controller.extend(AuthProvider, {
this.set('invalidCredentials', true);
});
}
// if (this.get('isAuthProviderCAS')) {
//
// this.get('session').authenticate('authenticator:cas').then((response) => {
// this.transitionToRoute('folders');
// return response;
// }).catch(() => {
// this.set('invalidCredentials', true);
// });
// }
}
}
});

View file

@ -17,6 +17,7 @@ import Route from '@ember/routing/route';
export default Route.extend({
appMeta: service(),
kcAuth: service(),
global: service(),
localStorage: service(),
showLogin: false,
@ -40,6 +41,13 @@ export default Route.extend({
});
break;
case constants.AuthProvider.CAS: {
let config = JSON.parse(this.get('appMeta.authConfig'));
let url = config.url + '/login?service=' + encodeURIComponent(config.redirectUrl);
window.location.replace(url);
resolve();
break;
}
default:
this.set('showLogin', true);

View file

@ -27,8 +27,8 @@
{{input type="password" value=password id="authPassword" class="form-control" autocomplete="current-password"}}
{{/if}}
</div>
{{ui/ui-button color=constants.Color.Green light=true label=constants.Label.SignIn onClick=(action "login")}}
{{ui/ui-button color=constants.Color.Green light=true label=constants.Label.SignIn onClick=(action "login")}}
<div class="{{unless invalidCredentials "invisible"}} color-red-600 mt-3">Invalid credentials</div>
{{#if isAuthProviderDocumize}}

View file

@ -42,6 +42,9 @@ export default Route.extend(AuthenticatedRouteMixin, {
case constants.AuthProvider.LDAP:
data.authConfig = config;
break;
case constants.AuthProvider.CAS:
data.authConfig = config;
break;
case constants.AuthProvider.Documize:
data.authConfig = '';
break;

View file

@ -1,6 +1,6 @@
{{layout/logo-heading
title="Authentication"
desc="Choose user authentication provider — Documize, Redhat Keycloak, LDAP/AD"
desc="Choose user authentication provider — Documize, Redhat Keycloak, LDAP/AD, Central Authentication Server"
icon=constants.Icon.Locked}}
{{customize/auth-settings

View file

@ -160,6 +160,9 @@ export default Router.map(function () {
this.route('share', {
path: 'share/:id/:slug/:serial'
});
this.route('cas', {
path: 'cas'
});
}
);

View file

@ -26,7 +26,7 @@ export default Route.extend(ApplicationRouteMixin, {
let sa = this.get('session.session.authenticator');
return this.get('appMeta').boot(transition.targetName, '').then(data => {
if (sa !== "authenticator:documize" && sa !== "authenticator:keycloak" && sa !== "authenticator:ldap" && data.allowAnonymousAccess) {
if (sa !== "authenticator:documize" && sa !== "authenticator:keycloak" && sa !== "authenticator:ldap" && sa != "authenticator:cas" && data.allowAnonymousAccess) {
if (!this.get('appMeta.setupMode') && !this.get('appMeta.secureMode')) {
return this.get('session').authenticate('authenticator:anonymous', data);
}

View file

@ -164,7 +164,7 @@ export default BaseService.extend({
// fetchXXX represents UI specific bulk data loading designed to
// reduce network traffic and boost app performance.
// This method that returns:
// This method returns:
// 1. getUserVisible()
// 2. getSummary()
// 3. getSpaceCategoryMembership()

View file

@ -412,7 +412,7 @@ export default Service.extend({
// fetchXXX represents UI specific bulk data loading designed to
// reduce network traffic and boost app performance.
// This method that returns:
// This method returns:
// 1. getUserVisible()
// 2. getSummary()
// 3. getSpaceCategoryMembership()

View file

@ -18,11 +18,18 @@
</li>
<li class="option {{if isLDAPProvider "selected"}}" {{action "onLDAP"}}>
<div class="text-header">LDAP</div>
<div class="text">Connect to LDAP/ Active Directory</div>
<div class="text">Connect to LDAP/Active Directory</div>
{{#if isLDAPProvider}}
<i class="dicon {{constants.Icon.Tick}}" />
{{/if}}
</li>
<li class="option {{if isCASProvider "selected"}}" {{action "onCAS"}}>
<div class="text-header">CAS</div>
<div class="text">Via Central Authentication Server</div>
{{#if isCASProvider}}
<i class="dicon {{constants.Icon.Tick}}" />
{{/if}}
</li>
</ul>
</div>
@ -167,7 +174,18 @@
{{ui/ui-button color=constants.Color.Yellow light=true label="Test →" onClick=(action "onLDAPPreview")}}
{{ui/ui-button-gap}}
{{/if}}
{{#if isCASProvider}}
<div class="form-group">
<label for="cas-url">CAS Server URL</label>
{{focus-input id="cas-url" type="text" value=casConfig.url class=(if casErrorUrl "form-control is-invalid" "form-control")}}
<small class="form-text text-muted">e.g. http://localhost:8888/auth</small>
</div>
<div class="form-group">
<label for="cas-redirect-url">Documize CAS URL</label>
{{focus-input id="cas-redirect-url" type="text" value=casConfig.redirectUrl class=(if casErrorRedirectUrl "form-control is-invalid" "form-control")}}
<small class="form-text text-muted">e.g. http://<Documize URL>/auth/cas</small>
</div>
{{/if}}
{{ui/ui-button color=constants.Color.Green light=true icon=constants.Icon.Locked label=constants.Label.Activate onClick=(action "onSave")}}
</form>

28267
gui/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff