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 <pzamp...@apache.org>
Authored: Mon Mar 12 11:16:49 2018 -0400
Committer: Phil Zampino <pzamp...@apache.org>
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">
+                &nbsp;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

Reply via email to