KNOX-1202 - Descriptor and Provider Configuration Wizard Input Validation
Project: http://git-wip-us.apache.org/repos/asf/knox/repo Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/97d865e5 Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/97d865e5 Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/97d865e5 Branch: refs/heads/master Commit: 97d865e51ee51b9b0d300dcb8dd90b29fa2a3e4b Parents: 4c1cb80 Author: Phil Zampino <[email protected]> Authored: Mon Mar 12 11:16:49 2018 -0400 Committer: Phil Zampino <[email protected]> Committed: Tue Mar 13 21:33:27 2018 -0400 ---------------------------------------------------------------------- gateway-admin-ui/package.json | 11 +- gateway-admin-ui/src/app/app.module.ts | 2 + .../new-desc-wizard.component.html | 2 +- .../new-desc-wizard.component.ts | 13 ++ .../acls-authzn-provider-config.ts | 12 ++ .../authentication-wizard.ts | 3 +- .../authorization-wizard.ts | 1 + .../cas-provider-config.ts | 36 +++++ .../provider-config-wizard/category-wizard.ts | 4 + .../default-idassertion-provider-config.ts | 29 +++- .../display-binding-provider-config.ts | 24 ++++ .../identity-assertion-wizard.ts | 3 +- .../kerberos-provider-config.ts | 85 ++++++++++-- .../ldap-provider-config.ts | 67 +++++++-- .../oauth-provider-config.ts | 5 + .../oidc-provider-config.ts | 48 +++++++ .../pam-provider-config.ts | 13 ++ .../provider-config-wizard.component.html | 27 +++- .../provider-config-wizard.component.ts | 92 ++++++++++--- .../regex-idassertion-provider-config.ts | 24 +++- .../saml-provider-config.ts | 22 +++ .../sso-cookie-provider-config.ts | 4 + .../switchcase-idassertion-provider-config.ts | 22 ++- .../src/app/utils/validation-utils.ts | 135 +++++++++++++++++++ gateway-admin-ui/src/index.html | 6 +- .../applications/admin-ui/app/index.html | 2 +- .../app/inline.0c599dd7846e2462c94c.bundle.js | 1 + .../app/inline.823868e44971b225d6b4.bundle.js | 1 - .../app/main.31f4c13b664f32c9ce4d.bundle.js | 1 - .../app/main.bfb4b6a3d7d72f4c8841.bundle.js | 1 + 30 files changed, 628 insertions(+), 68 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/package.json ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/package.json b/gateway-admin-ui/package.json index 8fbfeb5..489a40e 100644 --- a/gateway-admin-ui/package.json +++ b/gateway-admin-ui/package.json @@ -21,14 +21,15 @@ "@angular/platform-browser-dynamic": "^5.0.0", "@angular/router": "^5.0.0", "core-js": "^2.4.1", - "rxjs": "^5.5.2", - "zone.js": "^0.8.14", + "jquery": ">=3.0.0", + "js-yaml": "^3.10.0", "ng2-ace-editor": "0.3.3", "ng2-bs3-modal": "^0.13.0", + "ng2-validation": "^4.2.0", + "popper.js": "^1.12.9", + "rxjs": "^5.5.2", "ts-helpers": "^1.1.1", - "jquery": ">=3.0.0", - "js-yaml": "^3.10.0", - "popper.js": "^1.12.9" + "zone.js": "^0.8.14" }, "devDependencies": { "@angular/cli": "^1.6.5", http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/app.module.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/app.module.ts b/gateway-admin-ui/src/app/app.module.ts index ede59e7..19e4347 100644 --- a/gateway-admin-ui/src/app/app.module.ts +++ b/gateway-admin-ui/src/app/app.module.ts @@ -18,6 +18,7 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { HttpClientModule } from "@angular/common/http"; import { FormsModule } from '@angular/forms'; +import { CustomFormsModule } from 'ng2-validation'; import { AppComponent } from './app.component'; import { TopologyService } from './topology.service'; @@ -46,6 +47,7 @@ import { ProviderConfigWizardComponent } from './provider-config-wizard/provider imports: [ BrowserModule, HttpClientModule, FormsModule, + CustomFormsModule, BsModalModule, AceEditorModule ], http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/new-desc-wizard/new-desc-wizard.component.html ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/new-desc-wizard/new-desc-wizard.component.html b/gateway-admin-ui/src/app/new-desc-wizard/new-desc-wizard.component.html index 4e6a407..4ab8bec 100644 --- a/gateway-admin-ui/src/app/new-desc-wizard/new-desc-wizard.component.html +++ b/gateway-admin-ui/src/app/new-desc-wizard/new-desc-wizard.component.html @@ -99,7 +99,7 @@ (click)="newDescriptorModal.dismiss()">Cancel</button> <button type="button" class="btn btn-primary btn-sm" - [disabled]="!descriptor || !descriptorName" + [disabled]="!descriptor || !descriptorName || !validate()" (click)="newDescriptorModal.close()">Ok</button> </bs-modal-footer> </bs-modal> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/new-desc-wizard/new-desc-wizard.component.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/new-desc-wizard/new-desc-wizard.component.ts b/gateway-admin-ui/src/app/new-desc-wizard/new-desc-wizard.component.ts index b697351..0759cae 100644 --- a/gateway-admin-ui/src/app/new-desc-wizard/new-desc-wizard.component.ts +++ b/gateway-admin-ui/src/app/new-desc-wizard/new-desc-wizard.component.ts @@ -6,6 +6,7 @@ import {Descriptor} from "../resource-detail/descriptor"; import {ResourceService} from "../resource/resource.service"; import {Resource} from "../resource/resource"; import {ResourceTypesService} from "../resourcetypes/resourcetypes.service"; +import {ValidationUtils} from "../utils/validation-utils"; @Component({ selector: 'app-new-desc-wizard', @@ -163,4 +164,16 @@ export class NewDescWizardComponent implements OnInit { this[propertyName] = !this[propertyName]; } + validate(): boolean { + let isValid: boolean = true; + + // Validate the discovery address + if (this.descriptor.discoveryAddress) { + isValid = isValid && ValidationUtils.isValidURL(this.descriptor.discoveryAddress); + isValid = isValid && ValidationUtils.isValidString(this.descriptor.discoveryCluster); + } + + return isValid; + } + } http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/acls-authzn-provider-config.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/acls-authzn-provider-config.ts b/gateway-admin-ui/src/app/provider-config-wizard/acls-authzn-provider-config.ts index 76dcdff..a7a5291 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/acls-authzn-provider-config.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/acls-authzn-provider-config.ts @@ -19,6 +19,8 @@ import {DisplayBindingProviderConfig} from "./display-binding-provider-config"; export class ACLsAuthznProviderConfig extends DisplayBindingProviderConfig { + private static MODE_VALUES: string[] = [ 'OR', 'AND' ]; + private static DEFAULT_MODE: string = 'Default Mode'; private static DEFAULT_ACL: string = 'Default ACL'; @@ -47,4 +49,14 @@ export class ACLsAuthznProviderConfig extends DisplayBindingProviderConfig { return ACLsAuthznProviderConfig.displayPropertyNameBindings.get(name); } + isValid(): boolean { + let isValid: boolean = true; + + let defaultMode = this.getParam(this.getDisplayNamePropertyBinding(ACLsAuthznProviderConfig.DEFAULT_MODE)); + if (defaultMode) { + isValid = (ACLsAuthznProviderConfig.MODE_VALUES.indexOf(defaultMode.toUpperCase()) > -1); + } + + return isValid; + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/authentication-wizard.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/authentication-wizard.ts b/gateway-admin-ui/src/app/provider-config-wizard/authentication-wizard.ts index 576fc56..dc9682b 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/authentication-wizard.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/authentication-wizard.ts @@ -38,7 +38,7 @@ export class AuthenticationWizard extends CategoryWizard { private static AUTH_HADOOP: string = 'Kerberos'; private static AUTH_SSO: string = 'SSO'; private static AUTH_SSO_COOKIE: string = 'SSO Cookie'; - private static AUTH_JWT: string = 'JWT'; + private static AUTH_JWT: string = 'JSON Web Tokens'; private static AUTH_CAS: string = 'CAS'; private static AUTH_OAUTH: string = 'OAuth'; private static AUTH_SAML: string = 'SAML'; @@ -82,6 +82,7 @@ export class AuthenticationWizard extends CategoryWizard { if (configType) { this.providerConfig = Object.create(configType.prototype) as AuthenticationProviderConfig; this.providerConfig.constructor.apply(this.providerConfig); + (this.providerConfig as AuthenticationProviderConfig).setType(this.selectedType); } else { console.debug('AuthenticationWizard --> No provider configuration type mapped for ' + this.selectedType); this.providerConfig = null; http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/authorization-wizard.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/authorization-wizard.ts b/gateway-admin-ui/src/app/provider-config-wizard/authorization-wizard.ts index 814282c..2361cbe 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/authorization-wizard.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/authorization-wizard.ts @@ -47,6 +47,7 @@ export class AuthorizationWizard extends CategoryWizard { if (configType) { this.providerConfig = Object.create(configType.prototype) as DisplayBindingProviderConfig; this.providerConfig.constructor.apply(this.providerConfig); + (this.providerConfig as DisplayBindingProviderConfig).setType(this.selectedType); } else { console.debug('AuthorizationWizard --> No provider configuration type mapped for ' + this.selectedType); this.providerConfig = null; http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/cas-provider-config.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/cas-provider-config.ts b/gateway-admin-ui/src/app/provider-config-wizard/cas-provider-config.ts index 6debee1..00a2437 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/cas-provider-config.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/cas-provider-config.ts @@ -16,6 +16,7 @@ */ import {AuthenticationProviderConfig} from "./authentication-provider-config"; +import {ValidationUtils} from "../utils/validation-utils"; export class CASProviderConfig extends AuthenticationProviderConfig { @@ -39,6 +40,8 @@ export class CASProviderConfig extends AuthenticationProviderConfig { ]); + private static SUPPORTED_PROTOCOLS: string[] = [ 'CAS10', 'CAS20', 'CAS20_PROXY', 'CAS30', 'CAS30_PROXY', 'SAML' ]; + constructor() { super('pac4j', AuthenticationProviderConfig.FEDERATION_ROLE); } @@ -51,4 +54,37 @@ export class CASProviderConfig extends AuthenticationProviderConfig { return CASProviderConfig.displayPropertyNameBindings.get(name); } + isValid(): boolean { + let isValid: boolean = true; + + let cbURL = this.getParam(this.getDisplayNamePropertyBinding(CASProviderConfig.CALLBACK_URL)); + if (cbURL) { + let isCBURLValid = ValidationUtils.isValidURL(cbURL); + if (!isCBURLValid) { + console.debug(CASProviderConfig.CALLBACK_URL + ' value is not a valid URL.'); + } + isValid = isValid && isCBURLValid; + } + + let loginURL = this.getParam(this.getDisplayNamePropertyBinding(CASProviderConfig.LOGIN_URL)); + if (loginURL) { + let isLoginURLValid = ValidationUtils.isValidURL(loginURL); + if (!isLoginURLValid) { + console.debug(CASProviderConfig.LOGIN_URL + ' value is not a valid URL.'); + } + isValid = isValid && isLoginURLValid; + } + + let protocol = this.getParam(this.getDisplayNamePropertyBinding(CASProviderConfig.PROTOCOL)); + if (protocol) { + let isProtocolValid = (CASProviderConfig.SUPPORTED_PROTOCOLS.indexOf(protocol) > -1); + if (!isProtocolValid) { + console.debug(CASProviderConfig.PROTOCOL + ' value is not a supported protocol'); + } + isValid = isValid && isProtocolValid; + } + + return isValid; + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/category-wizard.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/category-wizard.ts b/gateway-admin-ui/src/app/provider-config-wizard/category-wizard.ts index d6cc36b..0929c5a 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/category-wizard.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/category-wizard.ts @@ -23,6 +23,10 @@ export abstract class CategoryWizard { providerConfig: ProviderConfig; + reset() { + this.providerConfig = null; + this.selectedType = null; + } getSelectedType(): string { return this.selectedType; http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/default-idassertion-provider-config.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/default-idassertion-provider-config.ts b/gateway-admin-ui/src/app/provider-config-wizard/default-idassertion-provider-config.ts index 484e0d1..aa119a1 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/default-idassertion-provider-config.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/default-idassertion-provider-config.ts @@ -19,8 +19,8 @@ import {IdentityAssertionProviderConfig} from "./identity-assertion-provider-con export class DefaultIdAssertionProviderConfig extends IdentityAssertionProviderConfig { - static PRINCIPAL_MAPPING = 'Principal Mapping'; - static GROUP_PRINCIPAL_MAPPING = 'Group Principal Mapping'; + private static PRINCIPAL_MAPPING = 'Principal Mapping'; + private static GROUP_PRINCIPAL_MAPPING = 'Group Principal Mapping'; private static displayPropertyNames = [ DefaultIdAssertionProviderConfig.PRINCIPAL_MAPPING, DefaultIdAssertionProviderConfig.GROUP_PRINCIPAL_MAPPING @@ -32,6 +32,7 @@ export class DefaultIdAssertionProviderConfig extends IdentityAssertionProviderC [DefaultIdAssertionProviderConfig.GROUP_PRINCIPAL_MAPPING, 'group.principal.mapping'] ]); + private static MAPPING_REGEXP = new RegExp('^(?:(?:[\\w\\*\\,]*=(?:[\\w][^\\*\\=])+[;]?)*)$'); constructor() { console.debug('new DefaultIdAssertionProviderConfig()'); @@ -46,4 +47,28 @@ export class DefaultIdAssertionProviderConfig extends IdentityAssertionProviderC return DefaultIdAssertionProviderConfig.displayPropertyNameBindings.get(name); } + isValid(): boolean { + let isValid: boolean = true; + + let pMap = this.getParam(this.getDisplayNamePropertyBinding(DefaultIdAssertionProviderConfig.PRINCIPAL_MAPPING)); + if (pMap) { + let isPMapValid = DefaultIdAssertionProviderConfig.MAPPING_REGEXP.test(pMap); + if (!isPMapValid) { + console.debug(DefaultIdAssertionProviderConfig.PRINCIPAL_MAPPING + ' value is not a valid mapping'); + } + isValid = isValid && isPMapValid; + } + + let gpMap = this.getParam(this.getDisplayNamePropertyBinding(DefaultIdAssertionProviderConfig.GROUP_PRINCIPAL_MAPPING)); + if (gpMap) { + let isGMapValid = DefaultIdAssertionProviderConfig.MAPPING_REGEXP.test(gpMap); + if (!isGMapValid) { + console.debug(DefaultIdAssertionProviderConfig.GROUP_PRINCIPAL_MAPPING + ' value is not a valid mapping'); + } + isValid = isValid && isGMapValid; + } + + return isValid; + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/display-binding-provider-config.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/display-binding-provider-config.ts b/gateway-admin-ui/src/app/provider-config-wizard/display-binding-provider-config.ts index c39025f..9ded8f6 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/display-binding-provider-config.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/display-binding-provider-config.ts @@ -19,6 +19,16 @@ import {ProviderConfig} from "../resource-detail/provider-config"; export abstract class DisplayBindingProviderConfig extends ProviderConfig { + protected providerType: string; + + setType(type: string) { + this.providerType = type; + } + + getType(): string { + return this.providerType; + } + getName(): string { return this.name; } @@ -35,6 +45,12 @@ export abstract class DisplayBindingProviderConfig extends ProviderConfig { this.params[name] = value; } + removeParam(name: string): string { + let value = this.getParam(name); + delete this.params[name]; + return value; + } + getParamNames(): string[] { return Object.getOwnPropertyNames(this.params); } @@ -43,6 +59,14 @@ export abstract class DisplayBindingProviderConfig extends ProviderConfig { return this.params[name]; } + isPasswordParam(name: string): boolean { + return false; + } + + isValid(): boolean { + return true; + } + abstract getDisplayPropertyNames(): string[]; abstract getDisplayNamePropertyBinding(name: string): string; http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/identity-assertion-wizard.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/identity-assertion-wizard.ts b/gateway-admin-ui/src/app/provider-config-wizard/identity-assertion-wizard.ts index 180ed64..95970a0 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/identity-assertion-wizard.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/identity-assertion-wizard.ts @@ -29,7 +29,7 @@ export class IdentityAssertionWizard extends CategoryWizard { private stepCount: number = 4; private static DEFAULT: string = 'Default'; - private static CONCAT: string = 'Concatentation'; + private static CONCAT: string = 'Concatenation'; private static SWITCHCASE: string = 'SwitchCase'; private static REGEXP: string = 'Regular Expression'; private static GROUP_LOOKUP: string = 'Group Lookup'; @@ -62,6 +62,7 @@ export class IdentityAssertionWizard extends CategoryWizard { if (configType) { this.providerConfig = Object.create(configType.prototype) as IdentityAssertionProviderConfig; this.providerConfig.constructor.apply(this.providerConfig); + (this.providerConfig as IdentityAssertionProviderConfig).setType(this.selectedType); } else { console.debug('IdentityAssertionWizard --> No provider configuration type mapped for ' + this.selectedType); this.providerConfig = null; http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/kerberos-provider-config.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/kerberos-provider-config.ts b/gateway-admin-ui/src/app/provider-config-wizard/kerberos-provider-config.ts index 30dc49f..e615c44 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/kerberos-provider-config.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/kerberos-provider-config.ts @@ -16,6 +16,7 @@ */ import {AuthenticationProviderConfig} from "./authentication-provider-config"; +import {ValidationUtils} from "../utils/validation-utils"; export class KerberosProviderConfig extends AuthenticationProviderConfig { @@ -30,10 +31,8 @@ export class KerberosProviderConfig extends AuthenticationProviderConfig { static KRB_KEYTAB = 'KeyTab'; static KRB_RULES = 'Name Rules'; - private static displayPropertyNames = [ KerberosProviderConfig.CONFIG_PREFIX, KerberosProviderConfig.SIG_SECRET, - KerberosProviderConfig.TYPE, KerberosProviderConfig.ANON_ALLOWED, KerberosProviderConfig.TOKEN_VALIDITY, KerberosProviderConfig.COOKIE_DOMAIN, @@ -46,20 +45,22 @@ export class KerberosProviderConfig extends AuthenticationProviderConfig { private static displayPropertyNameBindings: Map<string, string> = new Map([ [KerberosProviderConfig.CONFIG_PREFIX, 'config.prefix'], - [KerberosProviderConfig.SIG_SECRET, '.signature.secret'], - [KerberosProviderConfig.TYPE, '.type'], - [KerberosProviderConfig.ANON_ALLOWED, '.simple.anonymous.allowed'], - [KerberosProviderConfig.TOKEN_VALIDITY, '.token.validity'], - [KerberosProviderConfig.COOKIE_DOMAIN, '.cookie.domain'], - [KerberosProviderConfig.COOKIE_PATH, '.cookie.path'], - [KerberosProviderConfig.KRB_PRINCIPAL, '.kerberos.principal'], - [KerberosProviderConfig.KRB_KEYTAB, '.kerberos.keytab'], - [KerberosProviderConfig.KRB_RULES, '.kerberos.name.rules'] + [KerberosProviderConfig.SIG_SECRET, 'signature.secret'], + [KerberosProviderConfig.TYPE, 'type'], + [KerberosProviderConfig.ANON_ALLOWED, 'simple.anonymous.allowed'], + [KerberosProviderConfig.TOKEN_VALIDITY, 'token.validity'], + [KerberosProviderConfig.COOKIE_DOMAIN, 'cookie.domain'], + [KerberosProviderConfig.COOKIE_PATH, 'cookie.path'], + [KerberosProviderConfig.KRB_PRINCIPAL, 'kerberos.principal'], + [KerberosProviderConfig.KRB_KEYTAB, 'kerberos.keytab'], + [KerberosProviderConfig.KRB_RULES, 'kerberos.name.rules'] ]); constructor() { super('HadoopAuth'); + this.setParam(this.getDisplayNamePropertyBinding(KerberosProviderConfig.CONFIG_PREFIX), 'hadoop.auth.config'); + this.setParam(this.getDisplayNamePropertyBinding(KerberosProviderConfig.TYPE), 'kerberos'); } getDisplayPropertyNames(): string[] { @@ -67,15 +68,71 @@ export class KerberosProviderConfig extends AuthenticationProviderConfig { } getDisplayNamePropertyBinding(name: string) { + let propName: string; if (name === KerberosProviderConfig.CONFIG_PREFIX) { - return KerberosProviderConfig.displayPropertyNameBindings.get(name); + propName = KerberosProviderConfig.displayPropertyNameBindings.get(name); } else { let prefix = this.getParam(KerberosProviderConfig.displayPropertyNameBindings.get(KerberosProviderConfig.CONFIG_PREFIX)); if (prefix) { - return prefix + KerberosProviderConfig.displayPropertyNameBindings.get(name); + propName = prefix + '.' + KerberosProviderConfig.displayPropertyNameBindings.get(name); + } else { + propName = KerberosProviderConfig.displayPropertyNameBindings.get(name); } } - return null; + return propName; + } + + isPasswordParam(name: string): boolean { + return (name === KerberosProviderConfig.SIG_SECRET); + } + + setParam(name: string, value: string) { + console.debug('KerberosProviderConfig --> setParam(' + name + ', ' + value + ')'); // TODO: PJZ: DELETE ME + if (name === this.getDisplayNamePropertyBinding(KerberosProviderConfig.CONFIG_PREFIX)) { + // If the config prefix property has changed, then the properties need to be modified accordingly + let prevPrefix = this.getParam(this.getDisplayNamePropertyBinding(KerberosProviderConfig.CONFIG_PREFIX)); + if (value !== prevPrefix) { + // Iterate over those properties which have already been set + for (let propName of this.getParamNames()) { + console.debug('\tPrevious property ' + propName); + if (propName.startsWith(prevPrefix)) { // If the property name includes the previous config prefix + let suffix = propName.substring(prevPrefix.length + 1); // prefix + '.' + let newPropName = value + '.' + suffix; + this.setParam(newPropName, this.getParam(propName)); + console.debug('\tRenamed ' + propName + ' to ' + newPropName + ' because config prefix changed.'); + this.removeParam(propName); + console.debug('\tDeleted ' + propName + ' because config prefix changed.'); + } + } + super.setParam(name, value); // Update the config prefix property itself + } + } else { + super.setParam(name, value); + } + } + + isValid(): boolean { + let isValid: boolean = true; + + let allowAnon = this.getParam(this.getDisplayNamePropertyBinding(KerberosProviderConfig.ANON_ALLOWED)); + if (allowAnon) { + let isValidAllowAnon = ValidationUtils.isValidBoolean(allowAnon); + if (!isValidAllowAnon) { + console.debug(KerberosProviderConfig.ANON_ALLOWED + ' value is not valid.'); + } + isValid = isValid && isValidAllowAnon; + } + + let tokenExpiration = this.getParam(this.getDisplayNamePropertyBinding(KerberosProviderConfig.TOKEN_VALIDITY)); + if (tokenExpiration) { + let isValidTokenExpiration = ValidationUtils.isValidNumber(tokenExpiration); + if (!isValidTokenExpiration) { + console.debug(KerberosProviderConfig.TOKEN_VALIDITY + ' value is not valid.'); + } + isValid = isValid && isValidTokenExpiration; + } + + return isValid; } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/ldap-provider-config.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/ldap-provider-config.ts b/gateway-admin-ui/src/app/provider-config-wizard/ldap-provider-config.ts index deb855a..719124a 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/ldap-provider-config.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/ldap-provider-config.ts @@ -17,17 +17,31 @@ import {AuthenticationProviderConfig} from "./authentication-provider-config"; import {OrderedParamContainer} from "./ordered-param-container"; +import {ValidationUtils} from "../utils/validation-utils"; export class LDAPProviderConfig extends AuthenticationProviderConfig implements OrderedParamContainer { - static SESSION_TIMEOUT = 'Session Timeout'; - static DN_TEMPLATE = 'User DN Template'; - static URL = 'URL'; - static MECHANISM = 'Mechanism'; - static REALM = 'Realm'; - static CONTEXT_FACTORY = 'LDAP Context Factory'; - static REALM_CONTEXT_FACTORY = 'Realm Context Factory'; - static AUTH_CHAIN = 'Authentication Chain'; + private static DN_TEMPLATE_REGEXP: RegExp = + new RegExp('(?:[A-Za-z][\\w-]*|\\d+(?:\\.\\d+)*)' + + '=(?:#(?:[\\dA-Fa-f]{2})+|(?:[^,=\\+<>#;\\"]|\\[,=\\+<>#;\\"]|\\[\\dA-Fa-f]{2})*|"(?:[^\\"]|\\[,=\\+<>#;\\"]|\\[\\dA-Fa-f]{2})*")' + + '(?:\\+(?:[A-Za-z][\\w-]*|\\d+(?:\\.\\d+)*)' + + '=(?:#(?:[\\dA-Fa-f]{2})' + + '+|(?:[^,=\\+<>#;\\"]|\\[,=\\+<>#;\\"]|\\[\\dA-Fa-f]{2})*|"(?:[^\\"]|\\[,=\\+<>#;\\"]|\\[\\dA-Fa-f]{2})*"))' + + '*(?:,(?:[A-Za-z][\\w-]*|\\d+(?:\\.\\d+)*)' + + '=(?:#(?:[\\dA-Fa-f]{2})+|(?:[^,=\\+<>#;\\"]|\\[,=\\+<>#;\\"]|\\[\\dA-Fa-f]{2})*|"(?:[^\\"]|\\[,=\\+<>#;\\"]|\\[\\dA-Fa-f]{2})*")' + + '(?:\\+(?:[A-Za-z][\\w-]*|\\d+(?:\\.\\d+)*)=(?:#(?:[\\dA-Fa-f]{2})+|(?:[^,=\\+<>#;\\"]|\\[,=\\+<>#;\\"]|\\[\\dA-Fa-f]{2})*|"(?:[^\\"]|\\[,=\\+<>#;\\"]|\\[\\dA-Fa-f]{2})*"))*)*'); + + + private static LDAP_URL_SCHEMES: string[] = [ 'ldap', 'ldaps' ]; + + private static SESSION_TIMEOUT = 'Session Timeout'; + private static DN_TEMPLATE = 'User DN Template'; + private static URL = 'URL'; + private static MECHANISM = 'Mechanism'; + private static REALM = 'Realm'; + private static CONTEXT_FACTORY = 'LDAP Context Factory'; + private static REALM_CONTEXT_FACTORY = 'Realm Context Factory'; + private static AUTH_CHAIN = 'Authentication Chain'; private static displayPropertyNames = [ LDAPProviderConfig.SESSION_TIMEOUT, LDAPProviderConfig.URL, @@ -84,7 +98,6 @@ export class LDAPProviderConfig extends AuthenticationProviderConfig implements return LDAPProviderConfig.displayPropertyNameBindings.get(name); } - getOrderedParamNames(): string[] { return LDAPProviderConfig.paramsOrder; } @@ -102,4 +115,40 @@ export class LDAPProviderConfig extends AuthenticationProviderConfig implements return result; } + isValid(): boolean { + let isValid: boolean = true; + + let timeout = this.getParam(this.getDisplayNamePropertyBinding(LDAPProviderConfig.SESSION_TIMEOUT)); + if (timeout) { + let isTimeoutValid = ValidationUtils.isValidNumber(timeout); + if (!isTimeoutValid) { + console.debug(LDAPProviderConfig.SESSION_TIMEOUT + ' value is not valid.'); + } + isValid = (isValid && isTimeoutValid); + } + + let url = this.getParam(this.getDisplayNamePropertyBinding(LDAPProviderConfig.URL)); + if (url) { + // Ensure that it's a valid LDAP(S) URL + let isURLValid = ValidationUtils.isValidURLOfScheme(url, LDAPProviderConfig.LDAP_URL_SCHEMES); + if (!isURLValid) { + console.debug(LDAPProviderConfig.URL + ' value is not valid.'); + } + isValid = isValid && isURLValid; + } else { + isValid = false; + } + + let dnTemplate = this.getParam(this.getDisplayNamePropertyBinding(LDAPProviderConfig.DN_TEMPLATE)); + if (dnTemplate) { + let isDNTemplateValid = LDAPProviderConfig.DN_TEMPLATE_REGEXP.test(dnTemplate); + if (!isDNTemplateValid) { + console.debug(LDAPProviderConfig.DN_TEMPLATE + ' value is not a valid DN template.'); + } + isValid = isValid && isDNTemplateValid; + } + + return isValid; + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/oauth-provider-config.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/oauth-provider-config.ts b/gateway-admin-ui/src/app/provider-config-wizard/oauth-provider-config.ts index 71df337..777690f 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/oauth-provider-config.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/oauth-provider-config.ts @@ -16,6 +16,7 @@ */ import {AuthenticationProviderConfig} from "./authentication-provider-config"; +import {ValidationUtils} from "../utils/validation-utils"; export class OAUTHProviderConfig extends AuthenticationProviderConfig { @@ -45,4 +46,8 @@ export class OAUTHProviderConfig extends AuthenticationProviderConfig { return OAUTHProviderConfig.displayPropertyNameBindings.get(name); } + isValid(): boolean { + return ValidationUtils.isValidURL(this.getParam(this.getDisplayNamePropertyBinding(OAUTHProviderConfig.CALLBACK_URL))); + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/oidc-provider-config.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/oidc-provider-config.ts b/gateway-admin-ui/src/app/provider-config-wizard/oidc-provider-config.ts index 8cf2e7f..00528f0 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/oidc-provider-config.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/oidc-provider-config.ts @@ -16,6 +16,8 @@ */ import {AuthenticationProviderConfig} from "./authentication-provider-config"; +import {ValidationUtils} from "../utils/validation-utils"; +import {SAMLProviderConfig} from "./saml-provider-config"; export class OIDCProviderConfig extends AuthenticationProviderConfig { @@ -64,4 +66,50 @@ export class OIDCProviderConfig extends AuthenticationProviderConfig { return OIDCProviderConfig.displayPropertyNameBindings.get(name); } + isPasswordParam(name: string): boolean { + return (name === OIDCProviderConfig.PROVIDER_SECRET); + } + + isValid(): boolean { + let isValid: boolean = true; + + let cbURL = this.getParam(this.getDisplayNamePropertyBinding(OIDCProviderConfig.CALLBACK_URL)); + if (cbURL) { + let isCBURLValid = ValidationUtils.isValidURL(cbURL); + if (!isCBURLValid) { + console.debug(OIDCProviderConfig.CALLBACK_URL + ' value is not a valid URL.'); + } + isValid = isValid && isCBURLValid; + } + + let pdURL = this.getParam(this.getDisplayNamePropertyBinding(OIDCProviderConfig.PROVIDER_DISCOVERY_URL)); + if (pdURL) { + let isPDURLValid = ValidationUtils.isValidURL(pdURL); + if (!isPDURLValid) { + console.debug(OIDCProviderConfig.PROVIDER_DISCOVERY_URL + ' value is not a valid URL.'); + } + isValid = isValid && isPDURLValid; + } + + let useNonce = this.getParam(this.getDisplayNamePropertyBinding(OIDCProviderConfig.USE_NONCE)); + if (useNonce) { + let isNonceValid = ValidationUtils.isValidBoolean(useNonce); + if (!isNonceValid) { + console.debug(OIDCProviderConfig.USE_NONCE + ' value is not a valid boolean.'); + } + isValid = isValid && isNonceValid; + } + + let clockSkew = this.getParam(this.getDisplayNamePropertyBinding(OIDCProviderConfig.MAX_CLOCK_SKEW)); + if (clockSkew) { + let isSkewValid = ValidationUtils.isValidNumber(clockSkew); + if (!isSkewValid) { + console.debug(OIDCProviderConfig.MAX_CLOCK_SKEW + ' value is not a valid number'); + } + isValid = isValid && isSkewValid; + } + + return isValid; + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/pam-provider-config.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/pam-provider-config.ts b/gateway-admin-ui/src/app/provider-config-wizard/pam-provider-config.ts index 55bc22c..243a4e0 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/pam-provider-config.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/pam-provider-config.ts @@ -17,6 +17,7 @@ import {AuthenticationProviderConfig} from "./authentication-provider-config"; import {OrderedParamContainer} from "./ordered-param-container"; +import {ValidationUtils} from "../utils/validation-utils"; export class PAMProviderConfig extends AuthenticationProviderConfig implements OrderedParamContainer { @@ -75,4 +76,16 @@ export class PAMProviderConfig extends AuthenticationProviderConfig implements O return result; } + isValid(): boolean { + let isValid = true; + + // Since the other properties are set internally, just validate the session timeout value + let timeout = this.getParam(this.getDisplayNamePropertyBinding(PAMProviderConfig.SESSION_TIMEOUT)); + if (timeout) { + isValid = ValidationUtils.isValidNumber(timeout); + } + + return isValid; + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/provider-config-wizard.component.html ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/provider-config-wizard.component.html b/gateway-admin-ui/src/app/provider-config-wizard/provider-config-wizard.component.html index db97d96..c6ffec8 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/provider-config-wizard.component.html +++ b/gateway-admin-ui/src/app/provider-config-wizard/provider-config-wizard.component.html @@ -11,8 +11,15 @@ <tr> <td><strong>Name</strong></td> <td><input type="textbox" + name="name" + #field="ngModel" + [required]="true" size="60" - [(ngModel)]="name"></td> + [(ngModel)]="name"> + <span *ngIf="field.errors?.required" style="color: red"> + A provider configuration name is required. + </span> + </td> </tr> </table> </div> <!-- Provider Configuration Name --> @@ -22,8 +29,8 @@ <!-- Display New Providers --> <div> <strong>Providers</strong><br/> - <div *ngFor="let pc of providers"> - {{pc.name}} + <div *ngFor="let pDisplay of getConfiguredProviderDisplayNames()"> + {{ pDisplay }} </div> </div> <!-- Display New Providers --> @@ -71,11 +78,21 @@ <table> <tr *ngFor="let pt of getProviderParams()"> <td><strong>{{pt}}</strong></td> - <td><input type="textbox" + <td> + <span> + <input [type]="!isPasswordParam(pt) || this['show'+pt] ? 'textbox' : 'password'" size="60" #paramInput (change)="setProviderParamBinding(pt, paramInput.value)" - [value]="getProviderParamBinding(pt)"></td> + [value]="getProviderParamBinding(pt)"> + </span> + <span *ngIf="isPasswordParam(pt)"> + <input type="checkbox" + class="clickable" + [checked]="getPasswordDisplay(pt)" + (click)="togglePasswordDisplay(pt)">show + </span> + </td> </tr> </table> </div> <!-- Provider Type Params Step --> http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/provider-config-wizard.component.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/provider-config-wizard.component.ts b/gateway-admin-ui/src/app/provider-config-wizard/provider-config-wizard.component.ts index 1453611..6fc9afe 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/provider-config-wizard.component.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/provider-config-wizard.component.ts @@ -83,6 +83,7 @@ export class ProviderConfigWizardComponent implements OnInit { } reset() { + this.getCategoryWizard(this.selectedCategory).reset(); this.step = 0; this.name = ''; this.providers = []; @@ -102,7 +103,7 @@ export class ProviderConfigWizardComponent implements OnInit { if (catWizard) { let pc: ProviderConfig = catWizard.getProviderConfig(); - if (pc) { + if (pc && this.isProviderConfigValid(pc)) { this.providers.push(pc); console.debug('ProviderConfigWizard --> Provider: name=' + pc.name + ', role=' + pc.role + ', enabled=' + pc.enabled); if (pc.params) { @@ -115,10 +116,13 @@ export class ProviderConfigWizardComponent implements OnInit { console.debug('\tParam: ' + name + ' = ' + pc.params[name]); } } + + this.step = 0; // Return to the beginning + + // Clear the wizard state + this.getCategoryWizard(this.selectedCategory).reset(); } } - - this.step = 0; // Return to the beginning } onClose() { @@ -131,18 +135,18 @@ export class ProviderConfigWizardComponent implements OnInit { newResource, this.resourceService.serializeProviderConfiguration(this.providers, 'json')) .then(() => { - // Reload the resource list presentation - this.resourceTypesService.selectResourceType('Provider Configurations'); - - // Set the new descriptor as the selected resource - this.resourceService.getProviderConfigResources().then(resources => { - for (let res of resources) { - if (res.name === newResource.name) { - this.resourceService.selectedResource(res); - break; + // Reload the resource list presentation + this.resourceTypesService.selectResourceType('Provider Configurations'); + + // Set the new descriptor as the selected resource + this.resourceService.getProviderConfigResources().then(resources => { + for (let res of resources) { + if (res.name === newResource.name) { + this.resourceService.selectedResource(res); + break; + } } - } - }); + }); }); } @@ -151,7 +155,7 @@ export class ProviderConfigWizardComponent implements OnInit { } onNextStep() { - ++this.step; + ++this.step; } onPreviousStep() { @@ -227,10 +231,13 @@ export class ProviderConfigWizardComponent implements OnInit { let pc = catWizard.getProviderConfig(); if (pc) { if (pc instanceof DisplayBindingProviderConfig) { - let dispPC = pc as DisplayBindingProviderConfig; - let property = dispPC.getDisplayNamePropertyBinding(name); - pc.setParam(property, value); - console.debug('ProviderConfigWizard --> Set ProviderConfig param value: ' + property + '=' + value); + let property = (pc as DisplayBindingProviderConfig).getDisplayNamePropertyBinding(name); + if (property) { + pc.setParam(property, value); + console.debug('ProviderConfigWizard --> Set ProviderConfig param value: ' + property + '=' + value); + } else { + console.debug('No provider property configured for ' + name); + } } } } @@ -251,4 +258,51 @@ export class ProviderConfigWizardComponent implements OnInit { return ''; } + getConfiguredProviderDisplayNames(): string[] { + let result: string[] = []; + + for (let p of this.providers) { + let pName: string; + let pRole: string; + if (p instanceof DisplayBindingProviderConfig) { + pName = (p as DisplayBindingProviderConfig).getType(); + pRole = p.getRole(); + } else { + pName = p.name; + pRole = p.role; + } + result.push(pName + ' (' + pRole + ')'); + } + + return result; + } + + isPasswordParam(name: string) { + let result: boolean = false; + + let p = this.getCategoryWizard().getProviderConfig(); + if (p && p instanceof DisplayBindingProviderConfig) { + result = (p as DisplayBindingProviderConfig).isPasswordParam(name); + } + + return result; + } + + isProviderConfigValid(pc: ProviderConfig): boolean { + let isValid: boolean = true; + if (pc instanceof DisplayBindingProviderConfig) { + console.debug('Checking validity of ' + this.selectedCategory + ' ' + (pc as DisplayBindingProviderConfig).getType() + ' provider...'); + isValid = (pc as DisplayBindingProviderConfig).isValid(); + } + return isValid; + } + + togglePasswordDisplay(propertyName: string) { + this['show' + propertyName] = !this['show' + propertyName]; + } + + getPasswordDisplay(propertName: string): boolean { + return this['show' + propertName]; + } + } http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/regex-idassertion-provider-config.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/regex-idassertion-provider-config.ts b/gateway-admin-ui/src/app/provider-config-wizard/regex-idassertion-provider-config.ts index a3a4e9b..8ca0f1f 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/regex-idassertion-provider-config.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/regex-idassertion-provider-config.ts @@ -16,13 +16,14 @@ */ import {IdentityAssertionProviderConfig} from "./identity-assertion-provider-config"; +import {ValidationUtils} from "../utils/validation-utils"; export class RegexAssertionProviderConfig extends IdentityAssertionProviderConfig { - static INPUT = 'Input'; - static OUTPUT = 'Output'; - static LOOKUP = "Lookup"; - static ORIG_ON_FAIL = "User Original Lookup on Failure"; + private static INPUT = 'Input'; + private static OUTPUT = 'Output'; + private static LOOKUP = "Lookup"; + private static ORIG_ON_FAIL = "Use Original Lookup on Failure"; private static displayPropertyNames = [ RegexAssertionProviderConfig.INPUT, RegexAssertionProviderConfig.OUTPUT, @@ -50,4 +51,19 @@ export class RegexAssertionProviderConfig extends IdentityAssertionProviderConfi return RegexAssertionProviderConfig.displayPropertyNameBindings.get(name); } + isValid(): boolean { + let isValid: boolean = true; + + let useOrig = this.getParam(this.getDisplayNamePropertyBinding(RegexAssertionProviderConfig.ORIG_ON_FAIL)); + if (useOrig) { + isValid = ValidationUtils.isValidBoolean(useOrig); + if (!isValid) { + console.debug('RegexAssertionProviderConfig --> ' + RegexAssertionProviderConfig.ORIG_ON_FAIL + + ' value is not a valid boolean.') + } + } + + return isValid; + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/saml-provider-config.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/saml-provider-config.ts b/gateway-admin-ui/src/app/provider-config-wizard/saml-provider-config.ts index c9cffc1..139cfe8 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/saml-provider-config.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/saml-provider-config.ts @@ -16,6 +16,7 @@ */ import {AuthenticationProviderConfig} from "./authentication-provider-config"; +import {ValidationUtils} from "../utils/validation-utils"; export class SAMLProviderConfig extends AuthenticationProviderConfig { @@ -55,6 +56,8 @@ export class SAMLProviderConfig extends AuthenticationProviderConfig { ]); + private static SECRET_PROPERTIES: string[] = [ SAMLProviderConfig.KEYSTORE_PASS, SAMLProviderConfig.PK_PASS ]; + constructor() { super('pac4j', AuthenticationProviderConfig.FEDERATION_ROLE); } @@ -67,4 +70,23 @@ export class SAMLProviderConfig extends AuthenticationProviderConfig { return SAMLProviderConfig.displayPropertyNameBindings.get(name); } + isPasswordParam(name: string): boolean { + return (name && SAMLProviderConfig.SECRET_PROPERTIES.indexOf(name) > -1); + } + + isValid(): boolean { + let isValid: boolean = true; + + let cb = this.getParam(this.getDisplayNamePropertyBinding(SAMLProviderConfig.CALLBACK_URL)); + if (cb) { + let isValidCB = ValidationUtils.isValidURL(cb); + if (!isValidCB) { + console.debug('SAMLProviderConfig --> ' + SAMLProviderConfig.CALLBACK_URL + ' value is not a valid URL.'); + } + isValid = isValidCB && isValidCB; + } + + return isValid; + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/sso-cookie-provider-config.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/sso-cookie-provider-config.ts b/gateway-admin-ui/src/app/provider-config-wizard/sso-cookie-provider-config.ts index bc54e6a..ba94197 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/sso-cookie-provider-config.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/sso-cookie-provider-config.ts @@ -16,6 +16,7 @@ */ import {AuthenticationProviderConfig} from "./authentication-provider-config"; +import {ValidationUtils} from "../utils/validation-utils"; export class SSOCookieProviderConfig extends AuthenticationProviderConfig { @@ -40,4 +41,7 @@ export class SSOCookieProviderConfig extends AuthenticationProviderConfig { return SSOCookieProviderConfig.displayPropertyNameBindings.get(name); } + isValid(): boolean { + return ValidationUtils.isValidURL(this.getParam(this.getDisplayNamePropertyBinding(SSOCookieProviderConfig.PROVIDER_URL))); + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/provider-config-wizard/switchcase-idassertion-provider-config.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/provider-config-wizard/switchcase-idassertion-provider-config.ts b/gateway-admin-ui/src/app/provider-config-wizard/switchcase-idassertion-provider-config.ts index 5284f13..3667ae0 100644 --- a/gateway-admin-ui/src/app/provider-config-wizard/switchcase-idassertion-provider-config.ts +++ b/gateway-admin-ui/src/app/provider-config-wizard/switchcase-idassertion-provider-config.ts @@ -19,8 +19,10 @@ import {IdentityAssertionProviderConfig} from "./identity-assertion-provider-con export class SwitchCaseAssertionProviderConfig extends IdentityAssertionProviderConfig { - static PRINCIPAL_CASE = 'Principal Case'; - static GROUP_PRINCIPAL_CASE = 'Group Principal Case'; + private static CASE_VALUES: string[] = [ 'upper', 'lower' ]; + + private static PRINCIPAL_CASE = 'Principal Case'; + private static GROUP_PRINCIPAL_CASE = 'Group Principal Case'; private static displayPropertyNames = [ SwitchCaseAssertionProviderConfig.PRINCIPAL_CASE, SwitchCaseAssertionProviderConfig.GROUP_PRINCIPAL_CASE @@ -44,4 +46,20 @@ export class SwitchCaseAssertionProviderConfig extends IdentityAssertionProvider return SwitchCaseAssertionProviderConfig.displayPropertyNameBindings.get(name); } + isValid(): boolean { + let isValid: boolean = true; + + let pc = this.getParam(this.getDisplayNamePropertyBinding(SwitchCaseAssertionProviderConfig.PRINCIPAL_CASE)); + if (pc) { + isValid = isValid && (SwitchCaseAssertionProviderConfig.CASE_VALUES.indexOf(pc.toLowerCase()) > -1); + } + + let gpc = this.getParam(this.getDisplayNamePropertyBinding(SwitchCaseAssertionProviderConfig.GROUP_PRINCIPAL_CASE)); + if (gpc) { + isValid = isValid && (SwitchCaseAssertionProviderConfig.CASE_VALUES.indexOf(gpc.toLowerCase()) > -1); + } + + return isValid; + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/app/utils/validation-utils.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/utils/validation-utils.ts b/gateway-admin-ui/src/app/utils/validation-utils.ts new file mode 100644 index 0000000..f7dcfcc --- /dev/null +++ b/gateway-admin-ui/src/app/utils/validation-utils.ts @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +export class ParsedURL { + + static REGEXP: RegExp = new RegExp('^(([^:\/?#]+):)?\/\/(([^\/?#]+):([^\/?#]+))?([^?#]*)(\/?([^#]*))?(#(.*))?'); + + scheme: string; + host: string; + port: string; + path: string; + query: string; + fragment: string; + + static parse(url: string): ParsedURL { + let result: ParsedURL; + + let matches = url.match(ParsedURL.REGEXP); + if (matches && matches.length > 0) { + result = { + scheme: matches[2], + host: matches[4], + port: matches[5], + path: matches[6], + query: matches[7], + fragment: matches[9] + }; + } + return result; + } +} + + +export class ValidationUtils { + + + static parseBoolean(value: string): boolean { + let parsed: boolean; + if (value) { + try { + parsed = JSON.parse(value); + } catch (e) { + // just return undefined + } + } + return parsed; + } + + + static parseURL(url: string): ParsedURL { + return ParsedURL.parse(url); + } + + + static isValidNumber(value: string): boolean { + return (value && !isNaN(Number(value))); + } + + + static isValidBoolean(value: string): boolean { + return (ValidationUtils.parseBoolean(value) !== undefined); + } + + static isValidString(value: string): boolean { + return (value && value.length > 0); + } + + static isValidValue(validValues: string[], value: string): boolean { + let isValid: boolean = false; + if (value && validValues) { + if (validValues.indexOf(value) > -1) { + isValid = true; + } + } + return isValid; + } + + + static isValidURL(url: string): boolean { + let isValid: boolean = false; + + let parsedURL = ValidationUtils.parseURL(url); + if (parsedURL) { + // Make sure it has at least a valid scheme, host and port + if (parsedURL.scheme) { + if (parsedURL.host) { + if (parsedURL.port) { + isValid = ValidationUtils.isValidNumber(parsedURL.port); + } + } + } + } + + return isValid; + } + + + static isValidURLOfScheme(url: string, acceptableSchemes?: string[]): boolean { + let isValid: boolean = false; + + let parsedURL = ValidationUtils.parseURL(url); + if (parsedURL) { + // Make sure it has at least a valid scheme, host and port + if (parsedURL.scheme) { + if (acceptableSchemes && acceptableSchemes.indexOf(parsedURL.scheme) < 0) { + console.debug('\tParsed URL scheme is not among acceptable schemes'); + return false; + } + if (parsedURL.host) { + if (parsedURL.port) { + isValid = ValidationUtils.isValidNumber(parsedURL.port); + } + } + } + } + + return isValid; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-admin-ui/src/index.html ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/index.html b/gateway-admin-ui/src/index.html index 94d6e1b..6036348 100644 --- a/gateway-admin-ui/src/index.html +++ b/gateway-admin-ui/src/index.html @@ -57,8 +57,10 @@ <resource-management></resource-management> <footer class="footer"> - <div>Knox Manager Version 0.1.0</div> - <gateway-version></gateway-version> + <div class="container-fluid"> + <div>Knox Manager Version 0.1.0</div> + <gateway-version></gateway-version> + </div> </footer> </body> </html> http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-applications/src/main/resources/applications/admin-ui/app/index.html ---------------------------------------------------------------------- diff --git a/gateway-applications/src/main/resources/applications/admin-ui/app/index.html b/gateway-applications/src/main/resources/applications/admin-ui/app/index.html index 69cc800..52a28a4 100644 --- a/gateway-applications/src/main/resources/applications/admin-ui/app/index.html +++ b/gateway-applications/src/main/resources/applications/admin-ui/app/index.html @@ -11,4 +11,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ---><!doctype html><html><head><meta charset="utf-8"><title>Apache Knox Manager</title><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" type="image/x-icon" href="favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1"><!-- Latest compiled and minified CSS --><link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"><!-- Optional theme --><link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"><!-- Custom styles for this template --><link href="assets/sticky-footer.css" rel="stylesheet"><script src="https://ajax.googleapis.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script><!-- Latest compiled and minified JavaScript --><scr ipt src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script><script src="assets/vkbeautify.js"></script><link href="styles.2ee5b7f4cd59a6cf015e.bundle.css" rel="stylesheet"/></head><body><div class="navbar-wrapper"><div class="container-fluid"><nav class="navbar navbar-inverse navbar-static-top"><div class="container-fluid"><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"><span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span></button> <a class="navbar-brand" href="#"><img style="max-width:200px; margin-top: -9px;" src="assets/knox-logo-transparent.gif" alt="Apache Knox Manager"></a></div></div></nav></div><!-- Content --><resource-management></res ource-management><footer class="footer"><div>Knox Manager Version 0.1.0</div><gateway-version></gateway-version></footer><script type="text/javascript" src="inline.823868e44971b225d6b4.bundle.js"></script><script type="text/javascript" src="scripts.c50bb762c438ae0f8842.bundle.js"></script><script type="text/javascript" src="main.31f4c13b664f32c9ce4d.bundle.js"></script></div></body></html> \ No newline at end of file +--><!doctype html><html><head><meta charset="utf-8"><title>Apache Knox Manager</title><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" type="image/x-icon" href="favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1"><!-- Latest compiled and minified CSS --><link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"><!-- Optional theme --><link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"><!-- Custom styles for this template --><link href="assets/sticky-footer.css" rel="stylesheet"><script src="https://ajax.googleapis.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script><!-- Latest compiled and minified JavaScript --><scr ipt src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script><script src="assets/vkbeautify.js"></script><link href="styles.2ee5b7f4cd59a6cf015e.bundle.css" rel="stylesheet"/></head><body><div class="navbar-wrapper"><div class="container-fluid"><nav class="navbar navbar-inverse navbar-static-top"><div class="container-fluid"><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"><span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span></button> <a class="navbar-brand" href="#"><img style="max-width:200px; margin-top: -9px;" src="assets/knox-logo-transparent.gif" alt="Apache Knox Manager"></a></div></div></nav></div><!-- Content --><resource-management></res ource-management><footer class="footer"><div class="container-fluid"><div>Knox Manager Version 0.1.0</div><gateway-version></gateway-version></div></footer><script type="text/javascript" src="inline.0c599dd7846e2462c94c.bundle.js"></script><script type="text/javascript" src="scripts.c50bb762c438ae0f8842.bundle.js"></script><script type="text/javascript" src="main.bfb4b6a3d7d72f4c8841.bundle.js"></script></div></body></html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-applications/src/main/resources/applications/admin-ui/app/inline.0c599dd7846e2462c94c.bundle.js ---------------------------------------------------------------------- diff --git a/gateway-applications/src/main/resources/applications/admin-ui/app/inline.0c599dd7846e2462c94c.bundle.js b/gateway-applications/src/main/resources/applications/admin-ui/app/inline.0c599dd7846e2462c94c.bundle.js new file mode 100644 index 0000000..074de3a --- /dev/null +++ b/gateway-applications/src/main/resources/applications/admin-ui/app/inline.0c599dd7846e2462c94c.bundle.js @@ -0,0 +1 @@ +!function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,c,u){for(var a,i,f,l=0,s=[];l<r.length;l++)t[i=r[l]]&&s.push(t[i][0]),t[i]=0;for(a in c)Object.prototype.hasOwnProperty.call(c,a)&&(e[a]=c[a]);for(n&&n(r,c,u);s.length;)s.shift()();if(u)for(l=0;l<u.length;l++)f=o(o.s=u[l]);return f};var r={},t={2:0};function o(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.e=function(e){var n=t[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,o){n=t[e]=[r,o]});n[2]=r;var c=document.getElementsByTagName("head")[0],u=document.createElement("script");u.type="text/javascript",u.charset="utf-8",u.async=!0,u.timeout=12e4,o.nc&&u.setAttribute("nonce",o.nc),u.src=o.p+""+e+"."+{0:"bfb4b6a3d7d72f4c8841",1:"aed76669724804835353"}[e]+".chunk.js";var a=setTimeout(i,12e4);function i(){u.onerror=u.onload=null,clearTimeout(a);var n=t[e];0!==n&&(n&&n[1](new Error("Loading chu nk "+e+" failed.")),t[e]=void 0)}return u.onerror=u.onload=i,c.appendChild(u),r},o.m=e,o.c=r,o.d=function(e,n,r){o.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="",o.oe=function(e){throw console.error(e),e}}([]); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/97d865e5/gateway-applications/src/main/resources/applications/admin-ui/app/inline.823868e44971b225d6b4.bundle.js ---------------------------------------------------------------------- diff --git a/gateway-applications/src/main/resources/applications/admin-ui/app/inline.823868e44971b225d6b4.bundle.js b/gateway-applications/src/main/resources/applications/admin-ui/app/inline.823868e44971b225d6b4.bundle.js deleted file mode 100644 index d865f38..0000000 --- a/gateway-applications/src/main/resources/applications/admin-ui/app/inline.823868e44971b225d6b4.bundle.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,c,u){for(var a,i,f,l=0,s=[];l<r.length;l++)t[i=r[l]]&&s.push(t[i][0]),t[i]=0;for(a in c)Object.prototype.hasOwnProperty.call(c,a)&&(e[a]=c[a]);for(n&&n(r,c,u);s.length;)s.shift()();if(u)for(l=0;l<u.length;l++)f=o(o.s=u[l]);return f};var r={},t={2:0};function o(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.e=function(e){var n=t[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,o){n=t[e]=[r,o]});n[2]=r;var c=document.getElementsByTagName("head")[0],u=document.createElement("script");u.type="text/javascript",u.charset="utf-8",u.async=!0,u.timeout=12e4,o.nc&&u.setAttribute("nonce",o.nc),u.src=o.p+""+e+"."+{0:"31f4c13b664f32c9ce4d",1:"aed76669724804835353"}[e]+".chunk.js";var a=setTimeout(i,12e4);function i(){u.onerror=u.onload=null,clearTimeout(a);var n=t[e];0!==n&&(n&&n[1](new Error("Loading chu nk "+e+" failed.")),t[e]=void 0)}return u.onerror=u.onload=i,c.appendChild(u),r},o.m=e,o.c=r,o.d=function(e,n,r){o.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="",o.oe=function(e){throw console.error(e),e}}([]); \ No newline at end of file
