diff --git a/adapters/oidc/as7-eap6/as7-subsystem/src/main/java/org/keycloak/subsystem/as7/KeycloakAdapterConfigService.java b/adapters/oidc/as7-eap6/as7-subsystem/src/main/java/org/keycloak/subsystem/as7/KeycloakAdapterConfigService.java index 210473c28f..b0bd654ba3 100755 --- a/adapters/oidc/as7-eap6/as7-subsystem/src/main/java/org/keycloak/subsystem/as7/KeycloakAdapterConfigService.java +++ b/adapters/oidc/as7-eap6/as7-subsystem/src/main/java/org/keycloak/subsystem/as7/KeycloakAdapterConfigService.java @@ -165,6 +165,9 @@ public final class KeycloakAdapterConfigService { protected boolean isDeploymentConfigured(DeploymentUnit deploymentUnit) { ModelNode deployment = getSecureDeployment(deploymentUnit); + if (! deployment.isDefined()) { + return false; + } ModelNode resource = deployment.get(SecureDeploymentDefinition.RESOURCE.getName()); return resource.isDefined(); } @@ -202,7 +205,9 @@ public final class KeycloakAdapterConfigService { private ModelNode getSecureDeployment(DeploymentUnit deploymentUnit) { String deploymentName = preferredDeploymentName(deploymentUnit); - return this.secureDeployments.get(deploymentName); + return this.secureDeployments.containsKey(deploymentName) + ? this.secureDeployments.get(deploymentName) + : new ModelNode(); } // KEYCLOAK-3273: prefer module name if available diff --git a/adapters/oidc/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakAdapterConfigService.java b/adapters/oidc/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakAdapterConfigService.java index 8214c3f758..1ccd2a07d9 100755 --- a/adapters/oidc/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakAdapterConfigService.java +++ b/adapters/oidc/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakAdapterConfigService.java @@ -164,6 +164,9 @@ public final class KeycloakAdapterConfigService { protected boolean isDeploymentConfigured(DeploymentUnit deploymentUnit) { ModelNode deployment = getSecureDeployment(deploymentUnit); + if (! deployment.isDefined()) { + return false; + } ModelNode resource = deployment.get(SecureDeploymentDefinition.RESOURCE.getName()); return resource.isDefined(); } @@ -201,7 +204,9 @@ public final class KeycloakAdapterConfigService { private ModelNode getSecureDeployment(DeploymentUnit deploymentUnit) { String deploymentName = preferredDeploymentName(deploymentUnit); - return this.secureDeployments.get(deploymentName); + return this.secureDeployments.containsKey(deploymentName) + ? this.secureDeployments.get(deploymentName) + : new ModelNode(); } // KEYCLOAK-3273: prefer module name if available diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java index c30e3ed339..e96a5e51f8 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java @@ -164,6 +164,9 @@ public final class KeycloakAdapterConfigService { protected boolean isDeploymentConfigured(DeploymentUnit deploymentUnit) { ModelNode deployment = getSecureDeployment(deploymentUnit); + if (! deployment.isDefined()) { + return false; + } ModelNode resource = deployment.get(SecureDeploymentDefinition.RESOURCE.getName()); return resource.isDefined(); } @@ -201,7 +204,9 @@ public final class KeycloakAdapterConfigService { private ModelNode getSecureDeployment(DeploymentUnit deploymentUnit) { String deploymentName = preferredDeploymentName(deploymentUnit); - return this.secureDeployments.get(deploymentName); + return this.secureDeployments.containsKey(deploymentName) + ? this.secureDeployments.get(deploymentName) + : new ModelNode(); } // KEYCLOAK-3273: prefer module name if available diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java index 2a6e4d3833..d4ddc02e3d 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java @@ -85,11 +85,6 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader
  • true if the attributes are valid, false otherwise. - */ - public static boolean validateTruststoreSetIfRequired(ModelNode attributes) { - if (isSet(attributes, DISABLE_TRUST_MANAGER)) { - return true; - } - - if (isSet(attributes, SSL_REQUIRED) && attributes.get(SSL_REQUIRED.getName()).asString().equals("none")) { - return true; - } - - return isSet(attributes, TRUSTSTORE) && isSet(attributes, TRUSTSTORE_PASSWORD); - } - private static boolean isSet(ModelNode attributes, SimpleAttributeDefinition def) { ModelNode attribute = attributes.get(def.getName()); diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/extension/RealmDefinitionTestCase.java b/adapters/oidc/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/extension/RealmDefinitionTestCase.java deleted file mode 100755 index e938d4892e..0000000000 --- a/adapters/oidc/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/extension/RealmDefinitionTestCase.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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. - */ -package org.keycloak.subsystem.adapter.extension; - -import org.jboss.dmr.ModelNode; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -/** - * - * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc. - */ -public class RealmDefinitionTestCase { - - private ModelNode model; - - @Before - public void setUp() { - model = new ModelNode(); - model.get("realm").set("demo"); - model.get("resource").set("customer-portal"); - model.get("realm-public-key").set("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"); - model.get("auth-url").set("http://localhost:8080/auth-server/rest/realms/demo/protocol/openid-connect/login"); - model.get("code-url").set("http://localhost:8080/auth-server/rest/realms/demo/protocol/openid-connect/access/codes"); - model.get("expose-token").set(true); - ModelNode credential = new ModelNode(); - credential.get("password").set("password"); - model.get("credentials").set(credential); - } - - @Test - public void testIsTruststoreSetIfRequired() throws Exception { - model.get("ssl-required").set("none"); - model.get("disable-trust-manager").set(true); - Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model)); - - model.get("ssl-required").set("none"); - model.get("disable-trust-manager").set(false); - Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model)); - - model.get("ssl-required").set("all"); - model.get("disable-trust-manager").set(true); - Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model)); - - model.get("ssl-required").set("all"); - model.get("disable-trust-manager").set(false); - Assert.assertFalse(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model)); - - model.get("ssl-required").set("external"); - model.get("disable-trust-manager").set(false); - Assert.assertFalse(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model)); - - model.get("ssl-required").set("all"); - model.get("disable-trust-manager").set(false); - model.get("truststore").set("foo"); - Assert.assertFalse(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model)); - - model.get("ssl-required").set("all"); - model.get("disable-trust-manager").set(false); - model.get("truststore").set("foo"); - model.get("truststore-password").set("password"); - Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model)); - - model.get("ssl-required").set("external"); - model.get("disable-trust-manager").set(false); - model.get("truststore").set("foo"); - model.get("truststore-password").set("password"); - Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model)); - } - -} diff --git a/adapters/saml/undertow/pom.xml b/adapters/saml/undertow/pom.xml index 8488fc4929..142d78fe9a 100755 --- a/adapters/saml/undertow/pom.xml +++ b/adapters/saml/undertow/pom.xml @@ -31,12 +31,6 @@ - - org.jboss.logging - jboss-logging - ${jboss.logging.version} - provided - org.keycloak keycloak-saml-core diff --git a/core/src/main/java/org/keycloak/representations/idm/ConfigPropertyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ConfigPropertyRepresentation.java index 6558c410e1..0cd65e1e83 100755 --- a/core/src/main/java/org/keycloak/representations/idm/ConfigPropertyRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/ConfigPropertyRepresentation.java @@ -17,6 +17,8 @@ package org.keycloak.representations.idm; +import java.util.List; + /** * @author Bill Burke * @version $Revision: 1 $ @@ -27,6 +29,7 @@ public class ConfigPropertyRepresentation { protected String helpText; protected String type; protected Object defaultValue; + protected List options; protected boolean secret; public String getName() { @@ -69,6 +72,14 @@ public class ConfigPropertyRepresentation { this.helpText = helpText; } + public List getOptions() { + return options; + } + + public void setOptions(List options) { + this.options = options; + } + public boolean isSecret() { return secret; } diff --git a/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java index a394b3c5a9..681a03bbab 100644 --- a/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java @@ -35,6 +35,10 @@ public class UserConsentRepresentation { // Key is clientId, Value is list of granted roles of this client protected Map> grantedClientRoles; + private Long createdDate; + + private Long lastUpdatedDate; + public String getClientId() { return clientId; } @@ -66,4 +70,20 @@ public class UserConsentRepresentation { public void setGrantedClientRoles(Map> grantedClientRoles) { this.grantedClientRoles = grantedClientRoles; } + + public void setCreatedDate(Long createdDate) { + this.createdDate = createdDate; + } + + public Long getCreatedDate() { + return createdDate; + } + + public void setLastUpdatedDate(Long lastUpdatedDate) { + this.lastUpdatedDate = lastUpdatedDate; + } + + public Long getLastUpdatedDate() { + return lastUpdatedDate; + } } diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml index 16887c1178..cc6073ad77 100755 --- a/dependencies/server-all/pom.xml +++ b/dependencies/server-all/pom.xml @@ -31,29 +31,17 @@ Keycloak Dependencies Server All - - - 0.3.0.M1 - 1.0.0.v20140518 - 1.8.3 - 3.5 - 1.0 - 3.2.5 - 2.2.8.Final - 1.7 - 2.5.2 - 1.5.5 - 1.21 - 1.3 - 3.0.20 - 2.6 - 1.4.7 - 13.0.1 - 4.4.2 - 2.1.2 - 3.2.3 - 3.0 - + + + + org.jboss.integration-platform + jboss-integration-platform-bom + pom + import + ${version.jboss-integration-platform} + + + @@ -158,122 +146,98 @@ org.eclipse.aether aether-api - ${version.org.eclipse.aether} org.eclipse.aether aether-connector-basic - ${version.org.eclipse.aether} org.eclipse.aether aether-spi - ${version.org.eclipse.aether} org.eclipse.aether aether-impl - ${version.org.eclipse.aether} org.eclipse.aether aether-transport-file - ${version.org.eclipse.aether} org.eclipse.aether aether-transport-http - ${version.org.eclipse.aether} org.eclipse.aether aether-transport-wagon - ${version.org.eclipse.aether} org.eclipse.aether aether-util - ${version.org.eclipse.aether} org.apache.ant ant - ${version.org.apache.ant} org.apache.ant ant-launcher - ${version.org.apache.ant} org.antlr antlr-runtime - ${version.org.antlr} aopalliance aopalliance - ${version.aopalliance} org.apache.maven maven-aether-provider - ${version.org.apache.maven} org.apache.maven maven-artifact - ${version.org.apache.maven} org.apache.maven maven-compat - ${version.org.apache.maven} org.apache.maven maven-core - ${version.org.apache.maven} org.apache.maven maven-model - ${version.org.apache.maven} org.apache.maven maven-model-builder - ${version.org.apache.maven} org.apache.maven maven-plugin-api - ${version.org.apache.maven} org.apache.maven maven-repository-metadata - ${version.org.apache.maven} org.apache.maven maven-settings - ${version.org.apache.maven} org.apache.maven maven-settings-builder - ${version.org.apache.maven} org.mvel mvel2 - ${version.org.mvel} org.eclipse.sisu org.eclipse.sisu.inject - ${version.org.eclipse.sisu} com.google.inject @@ -281,15 +245,9 @@ - - com.google.inject.extensions - guice-servlet - ${version.com.google.inject.extensions.guice-servlet} - org.eclipse.sisu org.eclipse.sisu.plexus - ${version.org.eclipse.sisu} org.sonatype.sisu @@ -300,62 +258,50 @@ org.sonatype.plexus plexus-cipher - ${version.org.sonatype.plexus.plexus-cipher} org.codehaus.plexus plexus-classworlds - ${version.org.codehaus.plexus.plexus-classworlds} org.codehaus.plexus plexus-component-annotations - ${version.org.codehaus.plexus.plexus-component-annotations} org.codehaus.plexus plexus-interpolation - ${version.org.codehaus.plexus.plexus-interpolation} org.sonatype.plexus plexus-sec-dispatcher - ${version.org.codehaus.plexus.plexus-sec-dispatcher} org.codehaus.plexus plexus-utils - ${version.org.codehaus.plexus.plexus-utils} org.apache.maven.wagon wagon-http - ${version.org.apache.maven.wagon} org.apache.maven.wagon wagon-http-shared - ${version.org.apache.maven.wagon} org.apache.maven.wagon wagon-provider-api - ${version.org.apache.maven.wagon} com.thoughtworks.xstream xstream - ${version.com.thoughtworks.xstream} com.google.guava guava - ${version.com.google.guava} org.eclipse.jdt.core.compiler ecj - ${version.org.eclipse.jdt.core.compiler} org.apache.httpcomponents @@ -368,7 +314,6 @@ com.lowagie itext - ${version.com.lowagie} bouncycastle @@ -387,7 +332,6 @@ org.sonatype.sisu sisu-guice - ${version.org.sonatype.sisu} no_aop diff --git a/distribution/demo-dist/pom.xml b/distribution/demo-dist/pom.xml index 76c002eac0..1e7762280e 100755 --- a/distribution/demo-dist/pom.xml +++ b/distribution/demo-dist/pom.xml @@ -151,6 +151,24 @@ + + unpack + compile + + unpack + + + + + org.keycloak + keycloak-wildfly-server-subsystem + ${project.version} + jar + default-config/*.xml + + + + @@ -202,31 +220,7 @@ - - - org.apache.maven.plugins - maven-dependency-plugin - - - unpack - compile - - unpack - - - - - org.keycloak - keycloak-wildfly-server-subsystem - ${project.version} - jar - default-config/*.xml - - - - - - + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml index 8cf1cde89b..4f351fd7c0 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml @@ -27,11 +27,6 @@ - - - - - diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-themes/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-themes/main/module.xml deleted file mode 100755 index a0f6e1d553..0000000000 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-themes/main/module.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-extensions/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-extensions/main/module.xml index 3f0470cd61..d785cb7fe3 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-extensions/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-extensions/main/module.xml @@ -29,7 +29,6 @@ - diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/sonatype/sisu/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/sonatype/sisu/main/module.xml index 8808d91a0b..866a61850c 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/sonatype/sisu/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/sonatype/sisu/main/module.xml @@ -20,7 +20,6 @@ - diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/pom.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/pom.xml index 0950a3bb8e..c8de6168bf 100755 --- a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/pom.xml +++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/pom.xml @@ -79,10 +79,6 @@ org.keycloak keycloak-tomcat-adapter-spi - - org.keycloak - keycloak-jboss-adapter-core - org.bouncycastle bcprov-jdk15on diff --git a/distribution/server-dist/assembly.xml b/distribution/server-dist/assembly.xml index 9e24567733..0e58c83344 100755 --- a/distribution/server-dist/assembly.xml +++ b/distribution/server-dist/assembly.xml @@ -46,6 +46,7 @@ appclient/** copyright.txt README.txt + themes/** @@ -56,6 +57,14 @@ 0755 + + target/${project.build.finalName} + + + themes/** + + 0444 + src/main/welcome-content welcome-content diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/app/app.component.ts b/examples/demo-template/angular2-product-app/src/main/webapp/app/app.component.ts index a6955e71f1..cd8ba04a1f 100644 --- a/examples/demo-template/angular2-product-app/src/main/webapp/app/app.component.ts +++ b/examples/demo-template/angular2-product-app/src/main/webapp/app/app.component.ts @@ -39,27 +39,9 @@ export class AppComponent { } reloadData() { - //angular dont have http interceptor yet - - this.kc.getToken() - .then(token => { - let headers = new Headers({ - 'Accept': 'application/json', - 'Authorization': 'Bearer ' + token - }); - - let options = new RequestOptions({ headers }); - - this.http.get('/database/products', options) - .map(res => res.json()) - .subscribe(prods => this.products = prods, - error => console.log(error)); - }) - .catch(error => console.log(error)); - } - - private handleError(error: Response) { - console.error(error); - return Observable.throw(error.json().error || 'Server error'); + this.http.get('/database/products') + .map(res => res.json()) + .subscribe(prods => this.products = prods, + error => console.log(error)); } } diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/app/app.module.ts b/examples/demo-template/angular2-product-app/src/main/webapp/app/app.module.ts index 6d2891f935..fd6075fbd9 100644 --- a/examples/demo-template/angular2-product-app/src/main/webapp/app/app.module.ts +++ b/examples/demo-template/angular2-product-app/src/main/webapp/app/app.module.ts @@ -1,8 +1,9 @@ import {NgModule} from "@angular/core"; import {BrowserModule} from "@angular/platform-browser"; -import {HttpModule} from "@angular/http"; +import {HttpModule, Http, XHRBackend, RequestOptions} from '@angular/http'; import {KeycloakService} from "./keycloak.service"; import {AppComponent} from "./app.component"; +import {KeycloakHttp} from "./keycloak.http"; @NgModule({ imports: [ @@ -14,7 +15,18 @@ import {AppComponent} from "./app.component"; ], providers: [ KeycloakService, + + { + provide: Http, + useFactory: + ( + backend: XHRBackend, + defaultOptions: RequestOptions, + keycloakService: KeycloakService + ) => new KeycloakHttp(backend, defaultOptions, keycloakService), + deps: [XHRBackend, RequestOptions, KeycloakService] + } ], bootstrap: [ AppComponent ] }) -export class AppModule {} +export class AppModule {} \ No newline at end of file diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/app/keycloak.http.ts b/examples/demo-template/angular2-product-app/src/main/webapp/app/keycloak.http.ts new file mode 100644 index 0000000000..a7af71ae3c --- /dev/null +++ b/examples/demo-template/angular2-product-app/src/main/webapp/app/keycloak.http.ts @@ -0,0 +1,116 @@ +import {Injectable} from "@angular/core"; +import {Http, Request, ConnectionBackend, RequestOptions, RequestOptionsArgs, Response, Headers} from "@angular/http"; + +import {KeycloakService} from "./keycloak.service"; +import {Observable} from 'rxjs/Rx'; + +/** + * This provides a wrapper over the ng2 Http class that insures tokens are refreshed on each request. + */ +@Injectable() +export class KeycloakHttp extends Http { + constructor(_backend: ConnectionBackend, _defaultOptions: RequestOptions, private _keycloakService:KeycloakService) { + super(_backend, _defaultOptions); + } + + private setToken(options: RequestOptionsArgs) { + + if (options == null || KeycloakService.auth == null || KeycloakService.auth.authz == null || KeycloakService.auth.authz.token == null) { + console.log("Need a token, but no token is available, not setting bearer token."); + return; + } + + options.headers.set('Authorization', 'Bearer ' + KeycloakService.auth.authz.token); + } + + private configureRequest(f:Function, url:string | Request, options:RequestOptionsArgs, body?: any):Observable { + let tokenPromise:Promise = this._keycloakService.getToken(); + let tokenObservable:Observable = Observable.fromPromise(tokenPromise); + let tokenUpdateObservable:Observable = Observable.create((observer) => { + if (options == null) { + let headers = new Headers(); + options = new RequestOptions({ headers: headers }); + } + + this.setToken(options); + observer.next(); + observer.complete(); + }); + let requestObservable:Observable = Observable.create((observer) => { + let result; + if (body) { + result = f.apply(this, [url, body, options]); + } else { + result = f.apply(this, [url, options]); + } + + result.subscribe((response) => { + observer.next(response); + observer.complete(); + }); + }); + + return >Observable + .merge(tokenObservable, tokenUpdateObservable, requestObservable, 1) // Insure no concurrency in the merged Observables + .filter((response) => response instanceof Response); + } + + /** + * Performs any type of http request. First argument is required, and can either be a url or + * a {@link Request} instance. If the first argument is a url, an optional {@link RequestOptions} + * object can be provided as the 2nd argument. The options object will be merged with the values + * of {@link BaseRequestOptions} before performing the request. + */ + request(url: string | Request, options?: RequestOptionsArgs): Observable { + return this.configureRequest(super.request, url, options); + } + + /** + * Performs a request with `get` http method. + */ + get(url: string, options?: RequestOptionsArgs): Observable { + return this.configureRequest(super.get, url, options); + } + + /** + * Performs a request with `post` http method. + */ + post(url: string, body: any, options?: RequestOptionsArgs): Observable { + return this.configureRequest(super.post, url, options, body); + } + + /** + * Performs a request with `put` http method. + */ + put(url: string, body: any, options?: RequestOptionsArgs): Observable { + return this.configureRequest(super.put, url, options, body); + } + + /** + * Performs a request with `delete` http method. + */ + delete(url: string, options?: RequestOptionsArgs): Observable { + return this.configureRequest(super.delete, url, options); + } + + /** + * Performs a request with `patch` http method. + */ + patch(url: string, body: any, options?: RequestOptionsArgs): Observable { + return this.configureRequest(super.patch, url, options, body); + } + + /** + * Performs a request with `head` http method. + */ + head(url: string, options?: RequestOptionsArgs): Observable { + return this.configureRequest(super.head, url, options); + } + + /** + * Performs a request with `options` http method. + */ + options(url: string, options?: RequestOptionsArgs): Observable { + return this.configureRequest(super.options, url, options); + } +} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java index d681125f90..ccd4aa61dc 100755 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java @@ -17,6 +17,8 @@ package org.keycloak.federation.ldap.mappers; +import java.util.List; + import org.keycloak.Config; import org.keycloak.federation.ldap.LDAPFederationProvider; import org.keycloak.federation.ldap.LDAPFederationProviderFactory; @@ -75,13 +77,13 @@ public abstract class AbstractLDAPFederationMapperFactory implements UserFederat public void close() { } - public static ProviderConfigProperty createConfigProperty(String name, String label, String helpText, String type, Object defaultValue) { + public static ProviderConfigProperty createConfigProperty(String name, String label, String helpText, String type, List options) { ProviderConfigProperty configProperty = new ProviderConfigProperty(); configProperty.setName(name); configProperty.setLabel(label); configProperty.setHelpText(helpText); configProperty.setType(type); - configProperty.setDefaultValue(defaultValue); + configProperty.setOptions(options); return configProperty; } diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java index e27f3efd48..460ab62ced 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java @@ -48,6 +48,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import org.keycloak.models.RoleModel; /** * @author Marek Posolda @@ -560,6 +561,11 @@ public class GroupLDAPFederationMapper extends AbstractLDAPFederationMapper impl this.ldapUser = ldapUser; } + @Override + public boolean hasRole(RoleModel role) { + return super.hasRole(role) || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true); + } + @Override public Set getGroups() { Set ldapGroupMappings = getLDAPGroupMappingsConverted(); diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java index 2a23001fcb..9d6e2a1282 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java @@ -348,7 +348,8 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper imple @Override public boolean hasRole(RoleModel role) { Set roles = getRoleMappings(); - return KeycloakModelUtils.hasRole(roles, role); + return KeycloakModelUtils.hasRole(roles, role) + || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true); } @Override diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java index edbc1863b5..6030a66e8e 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java @@ -301,7 +301,7 @@ public class UserAdapter implements CachedUserModel { for (RoleModel mapping: mappings) { if (mapping.hasRole(role)) return true; } - return false; + return KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true); } @Override diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java index 9fa874d93e..547419e329 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java @@ -555,6 +555,8 @@ public class UserCacheSession implements UserCache { } UserConsentModel consentModel = new UserConsentModel(client); + consentModel.setCreatedDate(cachedConsent.getCreatedDate()); + consentModel.setLastUpdatedDate(cachedConsent.getLastUpdatedDate()); for (String roleId : cachedConsent.getRoleIds()) { RoleModel role = session.realms().getRoleById(roleId, realm); diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUserConsent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUserConsent.java index e57d456bff..4b24bd12ab 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUserConsent.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUserConsent.java @@ -32,6 +32,8 @@ public class CachedUserConsent { private final String clientDbId; private final Set protocolMappers = new HashSet<>(); private final Set roleIds = new HashSet<>(); + private final Long createdDate; + private final Long lastUpdatedDate; public CachedUserConsent(UserConsentModel consentModel) { this.clientDbId = consentModel.getClient().getId(); @@ -39,6 +41,8 @@ public class CachedUserConsent { for (RoleModel role : consentModel.getGrantedRoles()) { this.roleIds.add(role.getId()); } + this.createdDate = consentModel.getCreatedDate(); + this.lastUpdatedDate = consentModel.getLastUpdatedDate(); } public String getClientDbId() { @@ -52,4 +56,12 @@ public class CachedUserConsent { public Set getRoleIds() { return roleIds; } + + public Long getCreatedDate() { + return createdDate; + } + + public Long getLastUpdatedDate() { + return lastUpdatedDate; + } } diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java index 957c79a328..f3b304d7c9 100755 --- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java +++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java @@ -33,11 +33,19 @@ import org.keycloak.models.KeycloakSession; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.io.Writer; import java.lang.reflect.Method; import java.sql.Connection; import java.util.List; import java.util.Set; import liquibase.LabelExpression; +import liquibase.database.Database; +import liquibase.exception.DatabaseException; +import liquibase.executor.Executor; +import liquibase.executor.ExecutorService; +import liquibase.executor.LoggingExecutor; +import liquibase.statement.core.CreateDatabaseChangeLogTableStatement; +import liquibase.util.StreamUtil; /** * @author Stian Thorgersen @@ -96,7 +104,7 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider { protected void updateChangeSet(Liquibase liquibase, String changelog, File exportFile) throws LiquibaseException, IOException { - List changeSets = liquibase.listUnrunChangeSets((Contexts) null, new LabelExpression()); + List changeSets = getChangeSets(liquibase); if (!changeSets.isEmpty()) { List ranChangeSets = liquibase.getDatabase().getRanChangeSetList(); if (ranChangeSets.isEmpty()) { @@ -110,7 +118,12 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider { } if (exportFile != null) { - liquibase.update((Contexts) null, new FileWriter(exportFile)); + try (Writer exportWriter = new FileWriter(exportFile)) { + if (ranChangeSets.isEmpty()) { + outputChangeLogTableCreationScript(liquibase, exportWriter); + } + liquibase.update((Contexts) null, new LabelExpression(), exportWriter, false); + } } else { liquibase.update((Contexts) null); } @@ -125,6 +138,27 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider { } } + private void outputChangeLogTableCreationScript(Liquibase liquibase, final Writer exportWriter) throws DatabaseException { + Database database = liquibase.getDatabase(); + + Executor oldTemplate = ExecutorService.getInstance().getExecutor(database); + LoggingExecutor executor = new LoggingExecutor(ExecutorService.getInstance().getExecutor(database), exportWriter, database); + ExecutorService.getInstance().setExecutor(database, executor); + + executor.comment("*********************************************************************"); + executor.comment("* Keycloak database creation script - apply this script to empty DB *"); + executor.comment("*********************************************************************" + StreamUtil.getLineSeparator()); + + executor.execute(new CreateDatabaseChangeLogTableStatement()); + // DatabaseChangeLogLockTable is created before this code is executed and recreated if it does not exist automatically + // in org.keycloak.connections.jpa.updater.liquibase.lock.CustomLockService.init() called indirectly from + // KeycloakApplication constructor (search for waitForLock() call). Hence it is not included in the creation script. + + executor.comment("*********************************************************************" + StreamUtil.getLineSeparator()); + + ExecutorService.getInstance().setExecutor(database, oldTemplate); + } + @Override public Status validate(Connection connection, String defaultSchema) { logger.debug("Validating if database is updated"); @@ -160,7 +194,8 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider { } protected Status validateChangeSet(Liquibase liquibase, String changelog) throws LiquibaseException { - List changeSets = liquibase.listUnrunChangeSets((Contexts) null, new LabelExpression()); + List changeSets = getChangeSets(liquibase); + if (!changeSets.isEmpty()) { if (changeSets.size() == liquibase.getDatabaseChangeLog().getChangeSets().size()) { return Status.EMPTY; @@ -174,6 +209,15 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider { } } + @SuppressWarnings("unchecked") + private List getChangeSets(Liquibase liquibase) { + // TODO tracked as: https://issues.jboss.org/browse/KEYCLOAK-3730 + // TODO: When https://liquibase.jira.com/browse/CORE-2919 is resolved, replace the following two lines with: + // List changeSets = liquibase.listUnrunChangeSets((Contexts) null, new LabelExpression(), false); + Method listUnrunChangeSets = Reflections.findDeclaredMethod(Liquibase.class, "listUnrunChangeSets", Contexts.class, LabelExpression.class, boolean.class); + return Reflections.invokeMethod(true, listUnrunChangeSets, List.class, liquibase, (Contexts) null, new LabelExpression(), false); + } + private Liquibase getLiquibaseForKeycloakUpdate(Connection connection, String defaultSchema) throws LiquibaseException { LiquibaseConnectionProvider liquibaseProvider = session.getProvider(LiquibaseConnectionProvider.class); return liquibaseProvider.getLiquibase(connection, defaultSchema); diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java index 0336631752..bd03727522 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java @@ -18,6 +18,7 @@ package org.keycloak.models.jpa; import org.keycloak.common.util.MultivaluedHashMap; +import org.keycloak.common.util.Time; import org.keycloak.component.ComponentModel; import org.keycloak.credential.CredentialModel; import org.keycloak.credential.UserCredentialStore; @@ -201,10 +202,14 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { throw new ModelDuplicateException("Consent already exists for client [" + clientId + "] and user [" + userId + "]"); } + long currentTime = Time.currentTimeMillis(); + consentEntity = new UserConsentEntity(); consentEntity.setId(KeycloakModelUtils.generateId()); consentEntity.setUser(em.getReference(UserEntity.class, userId)); consentEntity.setClientId(clientId); + consentEntity.setCreatedDate(currentTime); + consentEntity.setLastUpdatedDate(currentTime); em.persist(consentEntity); em.flush(); @@ -277,6 +282,8 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { throw new ModelException("Client with id " + entity.getClientId() + " is not available"); } UserConsentModel model = new UserConsentModel(client); + model.setCreatedDate(entity.getCreatedDate()); + model.setLastUpdatedDate(entity.getLastUpdatedDate()); Collection grantedRoleEntities = entity.getGrantedRoles(); if (grantedRoleEntities != null) { @@ -346,6 +353,8 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore { em.remove(toRemove); } + consentEntity.setLastUpdatedDate(Time.currentTimeMillis()); + em.flush(); } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java index 7c5fe1ff0c..5ee5490282 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java @@ -352,7 +352,8 @@ public class UserAdapter implements UserModel, JpaModel { @Override public boolean hasRole(RoleModel role) { Set roles = getRoleMappings(); - return KeycloakModelUtils.hasRole(roles, role); + return KeycloakModelUtils.hasRole(roles, role) + || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true); } protected TypedQuery getUserRoleMappingEntityTypedQuery(RoleModel role) { diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java index 34c272a5b3..c2b7b021ef 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java @@ -68,6 +68,12 @@ public class UserConsentEntity { @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "userConsent") Collection grantedProtocolMappers = new ArrayList(); + @Column(name = "CREATED_DATE") + private Long createdDate; + + @Column(name = "LAST_UPDATED_DATE") + private Long lastUpdatedDate; + public String getId() { return id; } @@ -108,6 +114,22 @@ public class UserConsentEntity { this.grantedProtocolMappers = grantedProtocolMappers; } + public Long getCreatedDate() { + return createdDate; + } + + public void setCreatedDate(Long createdDate) { + this.createdDate = createdDate; + } + + public Long getLastUpdatedDate() { + return lastUpdatedDate; + } + + public void setLastUpdatedDate(Long lastUpdatedDate) { + this.lastUpdatedDate = lastUpdatedDate; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -125,5 +147,4 @@ public class UserConsentEntity { public int hashCode() { return id.hashCode(); } - } diff --git a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml index e6d2753624..cc4c9ff016 100644 --- a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml +++ b/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml @@ -36,4 +36,5 @@ + diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.3.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.3.0.xml index bc2ef13736..579aa04088 100755 --- a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.3.0.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.3.0.xml @@ -31,6 +31,7 @@ + @@ -47,7 +48,11 @@ + + + + + - \ No newline at end of file diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java index cb27cf5771..7b46c0c102 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java @@ -21,6 +21,7 @@ import com.mongodb.BasicDBObject; import com.mongodb.DBObject; import com.mongodb.QueryBuilder; import org.keycloak.common.util.MultivaluedHashMap; +import org.keycloak.common.util.Time; import org.keycloak.component.ComponentModel; import org.keycloak.connections.mongo.api.MongoStore; import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext; @@ -524,9 +525,13 @@ public class MongoUserProvider implements UserProvider, UserCredentialStore { throw new ModelDuplicateException("Consent already exists for client [" + clientId + "] and user [" + userId + "]"); } + long currentTime = Time.currentTimeMillis(); + MongoUserConsentEntity consentEntity = new MongoUserConsentEntity(); consentEntity.setUserId(userId); consentEntity.setClientId(clientId); + consentEntity.setCreatedDate(currentTime); + consentEntity.setLastUpdatedDate(currentTime); fillEntityFromModel(consent, consentEntity); getMongoStore().insertEntity(consentEntity, invocationContext); } @@ -568,6 +573,8 @@ public class MongoUserProvider implements UserProvider, UserCredentialStore { throw new ModelException("Client with id " + entity.getClientId() + " is not available"); } UserConsentModel model = new UserConsentModel(client); + model.setCreatedDate(entity.getCreatedDate()); + model.setLastUpdatedDate(entity.getLastUpdatedDate()); for (String roleId : entity.getGrantedRoles()) { RoleModel roleModel = realm.getRoleById(roleId); @@ -596,6 +603,7 @@ public class MongoUserProvider implements UserProvider, UserCredentialStore { protMapperIds.add(protMapperModel.getId()); } consentEntity.setGrantedProtocolMappers(protMapperIds); + consentEntity.setLastUpdatedDate(Time.currentTimeMillis()); } @Override diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java index 5453c0b4ee..972f4409b7 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java @@ -268,7 +268,8 @@ public class UserAdapter extends AbstractMongoAdapter implement @Override public boolean hasRole(RoleModel role) { Set roles = getRoleMappings(); - return KeycloakModelUtils.hasRole(roles, role); + return KeycloakModelUtils.hasRole(roles, role) + || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true); } @Override diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserConsentEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserConsentEntity.java index c60faee81a..1ca9e64f52 100644 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserConsentEntity.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserConsentEntity.java @@ -29,6 +29,8 @@ public class UserConsentEntity extends AbstractIdentifiableEntity { private String clientId; private List grantedRoles = new ArrayList(); private List grantedProtocolMappers = new ArrayList(); + private Long createdDate; + private Long lastUpdatedDate; public String getUserId() { return userId; @@ -61,4 +63,20 @@ public class UserConsentEntity extends AbstractIdentifiableEntity { public void setGrantedProtocolMappers(List grantedProtocolMappers) { this.grantedProtocolMappers = grantedProtocolMappers; } + + public Long getCreatedDate() { + return createdDate; + } + + public void setCreatedDate(Long createdDate) { + this.createdDate = createdDate; + } + + public Long getLastUpdatedDate() { + return lastUpdatedDate; + } + + public void setLastUpdatedDate(Long lastUpdatedDate) { + this.lastUpdatedDate = lastUpdatedDate; + } } diff --git a/pom.xml b/pom.xml index 81bbbecb4b..d928b9cd3f 100755 --- a/pom.xml +++ b/pom.xml @@ -80,6 +80,7 @@ 6.4.0.Final + 6.0.6.Final 2.0.0-M17 diff --git a/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java b/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java index e2b55e139e..a21aa65c7b 100755 --- a/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java +++ b/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java @@ -30,6 +30,7 @@ import org.keycloak.migration.migrators.MigrateTo1_9_2; import org.keycloak.migration.migrators.MigrateTo2_0_0; import org.keycloak.migration.migrators.MigrateTo2_1_0; import org.keycloak.migration.migrators.MigrateTo2_2_0; +import org.keycloak.migration.migrators.MigrateTo2_3_0; import org.keycloak.migration.migrators.Migration; import org.keycloak.models.KeycloakSession; @@ -53,6 +54,7 @@ public class MigrationModelManager { new MigrateTo2_0_0(), new MigrateTo2_1_0(), new MigrateTo2_2_0(), + new MigrateTo2_3_0(), }; public static void migrate(KeycloakSession session) { diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java new file mode 100644 index 0000000000..80862ca4f2 --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java @@ -0,0 +1,69 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed 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. + */ + +package org.keycloak.migration.migrators; + +import java.util.LinkedList; +import java.util.List; + +import org.keycloak.migration.ModelVersion; +import org.keycloak.models.ClientModel; +import org.keycloak.models.ClientTemplateModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ProtocolMapperContainerModel; +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.models.RealmModel; + +/** + * @author Marek Posolda + */ +public class MigrateTo2_3_0 implements Migration { + + public static final ModelVersion VERSION = new ModelVersion("2.3.0"); + + @Override + public void migrate(KeycloakSession session) { + for (RealmModel realm : session.realms().getRealms()) { + for (ClientModel client : realm.getClients()) { + updateProtocolMappers(client); + } + + for (ClientTemplateModel clientTemplate : realm.getClientTemplates()) { + updateProtocolMappers(clientTemplate); + } + } + } + + private void updateProtocolMappers(ProtocolMapperContainerModel client) { + List toUpdate = new LinkedList<>(); + for (ProtocolMapperModel mapper : client.getProtocolMappers()) { + if (!mapper.getConfig().containsKey("userinfo.token.claim") && mapper.getConfig().containsKey("id.token.claim")) { + mapper.getConfig().put("userinfo.token.claim", mapper.getConfig().get("id.token.claim")); + toUpdate.add(mapper); + } + } + + for (ProtocolMapperModel mapper : toUpdate) { + client.updateProtocolMapper(mapper); + } + } + + @Override + public ModelVersion getVersion() { + return VERSION; + } +} diff --git a/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java b/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java index 1a8d974062..cb1089192d 100755 --- a/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java +++ b/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java @@ -73,6 +73,8 @@ public interface KeycloakSession { Set getAllProviders(Class clazz); + Class getProviderClass(String providerClassName); + Object getAttribute(String attribute); Object removeAttribute(String attribute); void setAttribute(String name, Object value); diff --git a/server-spi/src/main/java/org/keycloak/models/RoleMapperModel.java b/server-spi/src/main/java/org/keycloak/models/RoleMapperModel.java index fd25372d7e..85f1fd3dfa 100755 --- a/server-spi/src/main/java/org/keycloak/models/RoleMapperModel.java +++ b/server-spi/src/main/java/org/keycloak/models/RoleMapperModel.java @@ -28,8 +28,24 @@ public interface RoleMapperModel { Set getClientRoleMappings(ClientModel app); + /** + * Returns {@code true} if this object is directly or indirectly assigned the given role, {@code false} otherwise. + *

    + * For example, {@code true} is returned for hasRole(R) if: + *

      + *
    • R is directly assigned to this object
    • + *
    • R is not assigned to this object but this object belongs to a group G which is assigned the role R
    • + *
    • R is not assigned to this object but this object belongs to a group G, and G belongs to group H which is assigned the role R
    • + *
    + * @param role + * @return see description + */ boolean hasRole(RoleModel role); + /** + * Grants the given role to this object. + * @param role + */ void grantRole(RoleModel role); Set getRoleMappings(); diff --git a/server-spi/src/main/java/org/keycloak/models/UserConsentModel.java b/server-spi/src/main/java/org/keycloak/models/UserConsentModel.java index d3c295b4ab..847696d575 100644 --- a/server-spi/src/main/java/org/keycloak/models/UserConsentModel.java +++ b/server-spi/src/main/java/org/keycloak/models/UserConsentModel.java @@ -28,6 +28,8 @@ public class UserConsentModel { private final ClientModel client; private Set protocolMappers = new HashSet(); private Set roles = new HashSet(); + private Long createdDate; + private Long lastUpdatedDate; public UserConsentModel(ClientModel client) { this.client = client; @@ -67,4 +69,19 @@ public class UserConsentModel { return false; } + public Long getCreatedDate() { + return createdDate; + } + + public void setCreatedDate(Long createdDate) { + this.createdDate = createdDate; + } + + public Long getLastUpdatedDate() { + return lastUpdatedDate; + } + + public void setLastUpdatedDate(Long lastUpdatedDate) { + this.lastUpdatedDate = lastUpdatedDate; + } } diff --git a/server-spi/src/main/java/org/keycloak/models/utils/ComponentUtil.java b/server-spi/src/main/java/org/keycloak/models/utils/ComponentUtil.java index a0d915edd7..507bfffb1d 100644 --- a/server-spi/src/main/java/org/keycloak/models/utils/ComponentUtil.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/ComponentUtil.java @@ -47,10 +47,8 @@ public class ComponentUtil { } public static ComponentFactory getComponentFactory(KeycloakSession session, ComponentModel component) { - Class provider; - try { - provider = (Class) Class.forName(component.getProviderType()); - } catch (ClassNotFoundException e) { + Class provider = session.getProviderClass(component.getProviderType()); + if (provider == null) { throw new RuntimeException("Invalid provider type '" + component.getProviderType() + "'"); } diff --git a/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java index 1eae85946c..230d15f137 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java @@ -70,6 +70,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.stream.StreamSupport; /** * Set of helper methods, which are useful in various model implementations. @@ -266,6 +267,43 @@ public final class KeycloakModelUtils { return false; } + /** + * Checks whether the {@code targetRole} is contained in the given group or its parents + * (if requested) + * @param group Group to check role for + * @param targetRole + * @param checkParentGroup When {@code true}, also parent group is recursively checked for role + * @return true if targetRole is in roles (directly or indirectly via composite role) + */ + public static boolean hasRoleFromGroup(GroupModel group, RoleModel targetRole, boolean checkParentGroup) { + if (group.hasRole(targetRole)) + return true; + + if (checkParentGroup) { + GroupModel parent = group.getParent(); + return parent != null && hasRoleFromGroup(parent, targetRole, true); + } + + return false; + } + + /** + * Checks whether the {@code targetRole} is contained in any of the {@code groups} or their parents + * (if requested) + * @param groups + * @param targetRole + * @param checkParentGroup When {@code true}, also parent group is recursively checked for role + * @return true if targetRole is in roles (directly or indirectly via composite role) + */ + public static boolean hasRoleFromGroup(Iterable groups, RoleModel targetRole, boolean checkParentGroup) { + if (groups == null) { + return false; + } + + return StreamSupport.stream(groups.spliterator(), false) + .anyMatch(group -> hasRoleFromGroup(group, targetRole, checkParentGroup)); + } + /** * * @param groups diff --git a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java index 8703a055cd..e647aec66c 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java @@ -710,6 +710,8 @@ public class ModelToRepresentation { consentRep.setGrantedProtocolMappers(grantedProtocolMappers); consentRep.setGrantedRealmRoles(grantedRealmRoles); consentRep.setGrantedClientRoles(grantedClientRoles); + consentRep.setCreatedDate(model.getCreatedDate()); + consentRep.setLastUpdatedDate(model.getLastUpdatedDate()); return consentRep; } @@ -780,6 +782,7 @@ public class ModelToRepresentation { propRep.setLabel(prop.getLabel()); propRep.setType(prop.getType()); propRep.setDefaultValue(prop.getDefaultValue()); + propRep.setOptions(prop.getOptions()); propRep.setHelpText(prop.getHelpText()); propRep.setSecret(prop.isSecret()); return propRep; diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index 83dd805678..f1e4ea7da6 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -1605,6 +1605,8 @@ public class RepresentationToModel { } UserConsentModel consentModel = new UserConsentModel(client); + consentModel.setCreatedDate(consentRep.getCreatedDate()); + consentModel.setLastUpdatedDate(consentRep.getLastUpdatedDate()); if (consentRep.getGrantedRealmRoles() != null) { for (String roleName : consentRep.getGrantedRealmRoles()) { diff --git a/server-spi/src/main/java/org/keycloak/provider/ProviderConfigProperty.java b/server-spi/src/main/java/org/keycloak/provider/ProviderConfigProperty.java index 5b824b3425..a6dbd77e91 100755 --- a/server-spi/src/main/java/org/keycloak/provider/ProviderConfigProperty.java +++ b/server-spi/src/main/java/org/keycloak/provider/ProviderConfigProperty.java @@ -17,6 +17,8 @@ package org.keycloak.provider; +import java.util.List; + /** * @author Bill Burke * @version $Revision: 1 $ @@ -46,6 +48,7 @@ public class ProviderConfigProperty { protected String helpText; protected String type = STRING_TYPE; protected Object defaultValue; + protected List options; protected boolean secret; public ProviderConfigProperty() { @@ -96,6 +99,14 @@ public class ProviderConfigProperty { this.defaultValue = defaultValue; } + public List getOptions() { + return options; + } + + public void setOptions(List options) { + this.options = options; + } + public String getHelpText() { return helpText; } diff --git a/server-spi/src/main/java/org/keycloak/provider/ProviderConfigurationBuilder.java b/server-spi/src/main/java/org/keycloak/provider/ProviderConfigurationBuilder.java index 5382355ce3..607a41b326 100644 --- a/server-spi/src/main/java/org/keycloak/provider/ProviderConfigurationBuilder.java +++ b/server-spi/src/main/java/org/keycloak/provider/ProviderConfigurationBuilder.java @@ -17,6 +17,7 @@ package org.keycloak.provider; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -43,14 +44,17 @@ public class ProviderConfigurationBuilder { return this; } - public ProviderConfigurationBuilder property(String name, String label, String helpText, String type, Object defaultValue, boolean secret) { + public ProviderConfigurationBuilder property(String name, String label, String helpText, String type, Object defaultValue, List options, boolean secret) { ProviderConfigProperty property = new ProviderConfigProperty(name, label, helpText, type, defaultValue); + property.setOptions(options); property.setSecret(secret); properties.add(property); return this; } - public ProviderConfigurationBuilder property(String name, String label, String helpText, String type, Object defaultValue) { - properties.add(new ProviderConfigProperty(name, label, helpText, type, defaultValue)); + public ProviderConfigurationBuilder property(String name, String label, String helpText, String type, Object defaultValue, List options) { + ProviderConfigProperty property = new ProviderConfigProperty(name, label, helpText, type, defaultValue); + property.setOptions(options); + properties.add(property); return this; } @@ -65,6 +69,7 @@ public class ProviderConfigurationBuilder { private String helpText; private String type; private Object defaultValue; + private List options; private boolean secret; public ProviderConfigPropertyBuilder name(String name) { @@ -92,6 +97,11 @@ public class ProviderConfigurationBuilder { return this; } + public ProviderConfigPropertyBuilder options(String... options) { + this.options = Arrays.asList(options); + return this; + } + public ProviderConfigPropertyBuilder secret(boolean secret) { this.secret = secret; return this; @@ -104,6 +114,7 @@ public class ProviderConfigurationBuilder { property.setHelpText(helpText); property.setType(type); property.setDefaultValue(defaultValue); + property.setOptions(options); property.setSecret(secret); ProviderConfigurationBuilder.this.properties.add(property); return ProviderConfigurationBuilder.this; diff --git a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java index 49d22889ed..df6c37ab2a 100644 --- a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java +++ b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java @@ -172,7 +172,8 @@ public abstract class AbstractUserAdapter implements UserModel { @Override public boolean hasRole(RoleModel role) { Set roles = getRoleMappings(); - return KeycloakModelUtils.hasRole(roles, role); + return KeycloakModelUtils.hasRole(roles, role) + || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true); } @Override diff --git a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java index 592b9ba4cb..865f454e2c 100644 --- a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java +++ b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java @@ -177,7 +177,8 @@ public abstract class AbstractUserAdapterFederatedStorage implements UserModel { @Override public boolean hasRole(RoleModel role) { Set roles = getRoleMappings(); - return KeycloakModelUtils.hasRole(roles, role); + return KeycloakModelUtils.hasRole(roles, role) + || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true); } @Override diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java index 7469511f86..3b409a04c5 100755 --- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java +++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java @@ -17,6 +17,7 @@ package org.keycloak.authentication; +import org.jboss.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.OAuth2Constants; import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator; @@ -63,7 +64,7 @@ import java.util.Map; */ public class AuthenticationProcessor { public static final String CURRENT_AUTHENTICATION_EXECUTION = "current.authentication.execution"; - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(AuthenticationProcessor.class); protected RealmModel realm; protected UserSessionModel userSession; protected ClientSessionModel clientSession; @@ -561,25 +562,25 @@ public class AuthenticationProcessor { if (failure instanceof AuthenticationFlowException) { AuthenticationFlowException e = (AuthenticationFlowException) failure; if (e.getError() == AuthenticationFlowError.INVALID_USER) { - logger.failedAuthentication(e); + ServicesLogger.LOGGER.failedAuthentication(e); event.error(Errors.USER_NOT_FOUND); return ErrorPage.error(session, Messages.INVALID_USER); } else if (e.getError() == AuthenticationFlowError.USER_DISABLED) { - logger.failedAuthentication(e); + ServicesLogger.LOGGER.failedAuthentication(e); event.error(Errors.USER_DISABLED); return ErrorPage.error(session, Messages.ACCOUNT_DISABLED); } else if (e.getError() == AuthenticationFlowError.USER_TEMPORARILY_DISABLED) { - logger.failedAuthentication(e); + ServicesLogger.LOGGER.failedAuthentication(e); event.error(Errors.USER_TEMPORARILY_DISABLED); return ErrorPage.error(session, Messages.INVALID_USER); } else if (e.getError() == AuthenticationFlowError.INVALID_CLIENT_SESSION) { - logger.failedAuthentication(e); + ServicesLogger.LOGGER.failedAuthentication(e); event.error(Errors.INVALID_CODE); return ErrorPage.error(session, Messages.INVALID_CODE); } else if (e.getError() == AuthenticationFlowError.EXPIRED_CODE) { - logger.failedAuthentication(e); + ServicesLogger.LOGGER.failedAuthentication(e); event.error(Errors.EXPIRED_CODE); return ErrorPage.error(session, Messages.EXPIRED_CODE); @@ -604,13 +605,13 @@ public class AuthenticationProcessor { return processor.authenticate(); } else { - logger.failedAuthentication(e); + ServicesLogger.LOGGER.failedAuthentication(e); event.error(Errors.INVALID_USER_CREDENTIALS); return ErrorPage.error(session, Messages.INVALID_USER); } } else { - logger.failedAuthentication(failure); + ServicesLogger.LOGGER.failedAuthentication(failure); event.error(Errors.INVALID_USER_CREDENTIALS); return ErrorPage.error(session, Messages.UNEXPECTED_ERROR_HANDLING_REQUEST); } @@ -620,7 +621,7 @@ public class AuthenticationProcessor { public Response handleClientAuthException(Exception failure) { if (failure instanceof AuthenticationFlowException) { AuthenticationFlowException e = (AuthenticationFlowException) failure; - logger.failedClientAuthentication(e); + ServicesLogger.LOGGER.failedClientAuthentication(e); if (e.getError() == AuthenticationFlowError.CLIENT_NOT_FOUND) { event.error(Errors.CLIENT_NOT_FOUND); return ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_client", "Could not find client"); @@ -635,7 +636,7 @@ public class AuthenticationProcessor { return ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "unauthorized_client", e.getError().toString() + ": " + e.getMessage()); } } else { - logger.errorAuthenticatingClient(failure); + ServicesLogger.LOGGER.errorAuthenticatingClient(failure); event.error(Errors.INVALID_CLIENT_CREDENTIALS); return ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "unauthorized_client", "Unexpected error when authenticating client: " + failure.getMessage()); } diff --git a/services/src/main/java/org/keycloak/authentication/ClientAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/ClientAuthenticationFlow.java index 4d3ae65290..0cac336cd9 100755 --- a/services/src/main/java/org/keycloak/authentication/ClientAuthenticationFlow.java +++ b/services/src/main/java/org/keycloak/authentication/ClientAuthenticationFlow.java @@ -17,6 +17,7 @@ package org.keycloak.authentication; +import org.jboss.logging.Logger; import org.keycloak.events.Details; import org.keycloak.events.Errors; import org.keycloak.models.AuthenticationExecutionModel; @@ -35,7 +36,7 @@ import java.util.List; */ public class ClientAuthenticationFlow implements AuthenticationFlow { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(ClientAuthenticationFlow.class); Response alternativeChallenge = null; AuthenticationProcessor processor; @@ -73,7 +74,7 @@ public class ClientAuthenticationFlow implements AuthenticationFlow { // Fallback to secret just in case (for backwards compatibility) if (expectedClientAuthType == null) { expectedClientAuthType = KeycloakModelUtils.getDefaultClientAuthenticatorType(); - logger.authMethodFallback(client.getClientId(), expectedClientAuthType); + ServicesLogger.LOGGER.authMethodFallback(client.getClientId(), expectedClientAuthType); } // Check if client authentication matches @@ -154,7 +155,7 @@ public class ClientAuthenticationFlow implements AuthenticationFlow { } else if (status == FlowStatus.FAILURE_CHALLENGE) { return sendChallenge(result, execution); } else { - logger.unknownResultStatus(); + ServicesLogger.LOGGER.unknownResultStatus(); throw new AuthenticationFlowException(AuthenticationFlowError.INTERNAL_ERROR); } } diff --git a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java index f406c486b2..d099277842 100755 --- a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java +++ b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java @@ -17,6 +17,7 @@ package org.keycloak.authentication; +import org.jboss.logging.Logger; import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.ClientSessionModel; @@ -32,7 +33,7 @@ import java.util.List; * @version $Revision: 1 $ */ public class DefaultAuthenticationFlow implements AuthenticationFlow { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(DefaultAuthenticationFlow.class); Response alternativeChallenge = null; AuthenticationExecutionModel challengedAlternativeExecution = null; boolean alternativeSuccessful = false; @@ -247,7 +248,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow { return processor.authenticate(); default: logger.debugv("authenticator INTERNAL_ERROR: {0}", execution.getAuthenticator()); - logger.unknownResultStatus(); + ServicesLogger.LOGGER.unknownResultStatus(); throw new AuthenticationFlowException(AuthenticationFlowError.INTERNAL_ERROR); } } diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpConfirmLinkAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpConfirmLinkAuthenticator.java index 407f8cc31f..82347004c6 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpConfirmLinkAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpConfirmLinkAuthenticator.java @@ -39,15 +39,13 @@ import javax.ws.rs.core.Response; */ public class IdpConfirmLinkAuthenticator extends AbstractIdpAuthenticator { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; - @Override protected void authenticateImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext serializedCtx, BrokeredIdentityContext brokerContext) { ClientSessionModel clientSession = context.getClientSession(); String existingUserInfo = clientSession.getNote(EXISTING_USER_INFO); if (existingUserInfo == null) { - logger.noDuplicationDetected(); + ServicesLogger.LOGGER.noDuplicationDetected(); context.attempted(); return; } diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpCreateUserIfUniqueAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpCreateUserIfUniqueAuthenticator.java index c39d975e16..9c35844f1c 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpCreateUserIfUniqueAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpCreateUserIfUniqueAuthenticator.java @@ -17,6 +17,7 @@ package org.keycloak.authentication.authenticators.broker; +import org.jboss.logging.Logger; import org.keycloak.authentication.AuthenticationFlowContext; import org.keycloak.authentication.authenticators.broker.util.ExistingUserInfo; import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext; @@ -39,7 +40,7 @@ import java.util.Map; */ public class IdpCreateUserIfUniqueAuthenticator extends AbstractIdpAuthenticator { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static Logger logger = Logger.getLogger(IdpCreateUserIfUniqueAuthenticator.class); @Override @@ -59,7 +60,7 @@ public class IdpCreateUserIfUniqueAuthenticator extends AbstractIdpAuthenticator String username = getUsername(context, serializedCtx, brokerContext); if (username == null) { - logger.resetFlow(realm.isRegistrationEmailAsUsername() ? "Email" : "Username"); + ServicesLogger.LOGGER.resetFlow(realm.isRegistrationEmailAsUsername() ? "Email" : "Username"); context.getClientSession().setNote(ENFORCE_UPDATE_PROFILE, "true"); context.resetFlow(); return; diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java index 936ac7b863..420eb20924 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java @@ -17,6 +17,7 @@ package org.keycloak.authentication.authenticators.broker; +import org.jboss.logging.Logger; import org.keycloak.authentication.AuthenticationFlowContext; import org.keycloak.authentication.AuthenticationFlowError; import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext; @@ -48,7 +49,7 @@ import java.util.concurrent.TimeUnit; */ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static Logger logger = Logger.getLogger(IdpEmailVerificationAuthenticator.class); @Override protected void authenticateImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext serializedCtx, BrokeredIdentityContext brokerContext) { @@ -57,7 +58,7 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator ClientSessionModel clientSession = context.getClientSession(); if (realm.getSmtpConfig().size() == 0) { - logger.smtpNotConfigured(); + ServicesLogger.LOGGER.smtpNotConfigured(); context.attempted(); return; } @@ -94,7 +95,7 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator } catch (EmailException e) { event.error(Errors.EMAIL_SEND_FAILED); - logger.confirmBrokerEmailFailed(e); + ServicesLogger.LOGGER.confirmBrokerEmailFailed(e); Response challenge = context.form() .setError(Messages.EMAIL_SENT_ERROR) .createErrorPage(); @@ -137,7 +138,7 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator context.setUser(existingUser); context.success(); } else { - logger.keyParamDoesNotMatch(); + ServicesLogger.LOGGER.keyParamDoesNotMatch(); Response challengeResponse = context.form() .setError(Messages.INVALID_ACCESS_CODE) .createErrorPage(); diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticator.java index 003983d148..c58e3e16c5 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticator.java @@ -17,6 +17,7 @@ package org.keycloak.authentication.authenticators.broker; +import org.jboss.logging.Logger; import org.keycloak.authentication.AuthenticationFlowContext; import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext; import org.keycloak.broker.provider.BrokeredIdentityContext; @@ -45,7 +46,7 @@ import java.util.List; */ public class IdpReviewProfileAuthenticator extends AbstractIdpAuthenticator { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(IdpReviewProfileAuthenticator.class); @Override public boolean requiresUser() { diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticatorFactory.java index 4937db3658..b293b716a3 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticatorFactory.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticatorFactory.java @@ -108,7 +108,8 @@ public class IdpReviewProfileAuthenticatorFactory implements AuthenticatorFactor property.setLabel("{{:: 'update-profile-on-first-login' | translate}}"); property.setType(ProviderConfigProperty.LIST_TYPE); List updateProfileValues = Arrays.asList(IdentityProviderRepresentation.UPFLM_ON, IdentityProviderRepresentation.UPFLM_MISSING, IdentityProviderRepresentation.UPFLM_OFF); - property.setDefaultValue(updateProfileValues); + property.setOptions(updateProfileValues); + property.setDefaultValue(IdentityProviderRepresentation.UPFLM_MISSING); property.setHelpText("Define conditions under which a user has to review and update his profile after first-time login. Value 'On' means that" + " page for reviewing profile will be displayed and user can review and update his profile. Value 'off' means that page won't be displayed." + " Value 'missing' means that page is displayed just when some required attribute is missing (wasn't downloaded from identity provider). Value 'missing' is the default one." diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java index 6c10318aa1..f88b674d53 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java @@ -17,6 +17,7 @@ package org.keycloak.authentication.authenticators.browser; +import org.jboss.logging.Logger; import org.keycloak.authentication.AbstractFormAuthenticator; import org.keycloak.authentication.AuthenticationFlowContext; import org.keycloak.authentication.AuthenticationFlowError; @@ -43,7 +44,7 @@ import java.util.List; */ public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuthenticator { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(AbstractUsernameFormAuthenticator.class); public static final String REGISTRATION_FORM_ACTION = "registration_form"; public static final String ATTEMPTED_USERNAME = "ATTEMPTED_USERNAME"; @@ -131,7 +132,7 @@ public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuth try { user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username); } catch (ModelDuplicateException mde) { - logger.modelDuplicateException(mde); + ServicesLogger.LOGGER.modelDuplicateException(mde); // Could happen during federation import if (mde.getDuplicateFieldName() != null && mde.getDuplicateFieldName().equals(UserModel.EMAIL)) { diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/ConditionalOtpFormAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/ConditionalOtpFormAuthenticatorFactory.java index f19cbb6547..f04ed0770b 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/ConditionalOtpFormAuthenticatorFactory.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/ConditionalOtpFormAuthenticatorFactory.java @@ -156,7 +156,7 @@ public class ConditionalOtpFormAuthenticatorFactory implements AuthenticatorFact defaultOutcome.setType(LIST_TYPE); defaultOutcome.setName(DEFAULT_OTP_OUTCOME); defaultOutcome.setLabel("Fallback OTP handling"); - defaultOutcome.setDefaultValue(asList(SKIP, FORCE)); + defaultOutcome.setOptions(asList(SKIP, FORCE)); defaultOutcome.setHelpText("What to do in case of every check abstains. Defaults to force OTP authentication."); return asList(forceOtpUserAttribute, skipOtpRole, forceOtpRole, skipOtpForHttpHeader, forceOtpForHttpHeader, defaultOutcome); diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/ScriptBasedAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/ScriptBasedAuthenticatorFactory.java index f9b2ff287a..b2e8b9872d 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/ScriptBasedAuthenticatorFactory.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/ScriptBasedAuthenticatorFactory.java @@ -20,10 +20,12 @@ import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.authentication.Authenticator; import org.keycloak.authentication.AuthenticatorFactory; +import org.keycloak.common.Profile; import org.keycloak.common.util.StreamUtil; import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.EnvironmentDependentProviderFactory; import org.keycloak.provider.ProviderConfigProperty; import java.io.IOException; @@ -41,7 +43,7 @@ import static org.keycloak.provider.ProviderConfigProperty.STRING_TYPE; * * @author Thomas Darimont */ -public class ScriptBasedAuthenticatorFactory implements AuthenticatorFactory { +public class ScriptBasedAuthenticatorFactory implements AuthenticatorFactory, EnvironmentDependentProviderFactory { private static final Logger LOGGER = Logger.getLogger(ScriptBasedAuthenticatorFactory.class); @@ -148,4 +150,9 @@ public class ScriptBasedAuthenticatorFactory implements AuthenticatorFactory { return asList(name, description, script); } + + @Override + public boolean isSupported() { + return Profile.isPreviewEnabled(); + } } diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java index c88f49208a..b7a8b6263b 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java @@ -17,6 +17,7 @@ package org.keycloak.authentication.authenticators.browser; +import org.jboss.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.authentication.AuthenticationFlowContext; import org.keycloak.authentication.AuthenticationFlowError; @@ -44,7 +45,7 @@ import java.util.Map; */ public class SpnegoAuthenticator extends AbstractUsernameFormAuthenticator implements Authenticator{ public static final String KERBEROS_DISABLED = "kerberos_disabled"; - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(SpnegoAuthenticator.class); @Override public boolean requiresUser() { diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java index 3bd63453b1..4f8e2d1948 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java @@ -37,7 +37,7 @@ import javax.ws.rs.core.Response; * @version $Revision: 1 $ */ public class UsernamePasswordForm extends AbstractUsernameFormAuthenticator implements Authenticator { - protected static ServicesLogger log = ServicesLogger.ROOT_LOGGER; + protected static ServicesLogger log = ServicesLogger.LOGGER; @Override public void action(AuthenticationFlowContext context) { diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientIdAndSecretAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientIdAndSecretAuthenticator.java index b7ecb41512..78f58da6d3 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientIdAndSecretAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientIdAndSecretAuthenticator.java @@ -25,7 +25,6 @@ import org.keycloak.models.ClientModel; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.representations.idm.CredentialRepresentation; -import org.keycloak.services.ServicesLogger; import org.keycloak.util.BasicAuthHelper; import javax.ws.rs.core.HttpHeaders; @@ -49,8 +48,6 @@ import java.util.Set; */ public class ClientIdAndSecretAuthenticator extends AbstractClientAuthenticator { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; - public static final String PROVIDER_ID = "client-secret"; public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = { diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java index 16a867d74c..2896c60ff3 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java @@ -59,8 +59,6 @@ import org.keycloak.services.Urls; */ public class JWTClientAuthenticator extends AbstractClientAuthenticator { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; - public static final String PROVIDER_ID = "client-jwt"; public static final String ATTR_PREFIX = "jwt.credential"; public static final String CERTIFICATE_ATTR = "jwt.credential.certificate"; @@ -156,7 +154,7 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator { context.success(); } catch (Exception e) { - logger.errorValidatingAssertion(e); + ServicesLogger.LOGGER.errorValidatingAssertion(e); Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "unauthorized_client", "Client authentication with signed JWT failed: " + e.getMessage()); context.failure(AuthenticationFlowError.INVALID_CLIENT_CREDENTIALS, challengeResponse); } diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java index 54bf8b9356..d9e8c4b2f0 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java @@ -43,7 +43,6 @@ import java.util.List; */ public class ValidateUsername extends AbstractDirectGrantAuthenticator { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; public static final String PROVIDER_ID = "direct-grant-validate-username"; @Override @@ -63,7 +62,7 @@ public class ValidateUsername extends AbstractDirectGrantAuthenticator { try { user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username); } catch (ModelDuplicateException mde) { - logger.modelDuplicateException(mde); + ServicesLogger.LOGGER.modelDuplicateException(mde); Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_request", "Invalid user credentials"); context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse); return; diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java index 3c575c8f30..d0919cc9e8 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java @@ -17,6 +17,7 @@ package org.keycloak.authentication.authenticators.resetcred; +import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.authentication.AuthenticationFlowContext; import org.keycloak.authentication.AuthenticationFlowError; @@ -46,7 +47,7 @@ import java.util.List; */ public class ResetCredentialChooseUser implements Authenticator, AuthenticatorFactory { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(ResetCredentialChooseUser.class); public static final String PROVIDER_ID = "reset-credentials-choose-user"; diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java index da4e7c08ea..d7e3e7494f 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java @@ -17,6 +17,7 @@ package org.keycloak.authentication.authenticators.resetcred; +import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.authentication.AuthenticationFlowContext; import org.keycloak.authentication.AuthenticationFlowError; @@ -53,7 +54,7 @@ import java.util.concurrent.TimeUnit; public class ResetCredentialEmail implements Authenticator, AuthenticatorFactory { public static final String RESET_CREDENTIAL_SECRET = "RESET_CREDENTIAL_SECRET"; - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(ResetCredentialEmail.class); public static final String PROVIDER_ID = "reset-credential-email"; @@ -100,7 +101,7 @@ public class ResetCredentialEmail implements Authenticator, AuthenticatorFactory .detail(Details.USERNAME, username) .user(user) .error(Errors.EMAIL_SEND_FAILED); - logger.failedToSendPwdResetEmail(e); + ServicesLogger.LOGGER.failedToSendPwdResetEmail(e); Response challenge = context.form() .setError(Messages.EMAIL_SENT_ERROR) .createErrorPage(); diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java index 26571e6a83..28747e054f 100755 --- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java +++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java @@ -23,6 +23,7 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.message.BasicNameValuePair; +import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.authentication.FormAction; import org.keycloak.authentication.FormActionFactory; @@ -62,7 +63,7 @@ public class RegistrationRecaptcha implements FormAction, FormActionFactory, Con public static final String RECAPTCHA_REFERENCE_CATEGORY = "recaptcha"; public static final String SITE_KEY = "site.key"; public static final String SITE_SECRET = "secret"; - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(RegistrationRecaptcha.class); public static final String PROVIDER_ID = "registration-recaptcha-action"; @@ -152,7 +153,7 @@ public class RegistrationRecaptcha implements FormAction, FormActionFactory, Con content.close(); } } catch (Exception e) { - logger.recaptchaFailed(e); + ServicesLogger.LOGGER.recaptchaFailed(e); } return success; } diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java index cafcd72898..a2c43f72ef 100755 --- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java +++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java @@ -17,6 +17,7 @@ package org.keycloak.authentication.requiredactions; +import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.authentication.RequiredActionContext; import org.keycloak.authentication.RequiredActionFactory; @@ -35,7 +36,6 @@ import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.ModelException; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.messages.Messages; import org.keycloak.services.validation.Validation; @@ -48,7 +48,7 @@ import java.util.concurrent.TimeUnit; * @version $Revision: 1 $ */ public class UpdatePassword implements RequiredActionProvider, RequiredActionFactory { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(UpdatePassword.class); @Override public void evaluateTriggers(RequiredActionContext context) { int daysToExpirePassword = context.getRealm().getPasswordPolicy().getDaysToExpirePassword(); diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java index 24b2547b74..fcc5df7f43 100644 --- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java +++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java @@ -29,7 +29,6 @@ import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.utils.FormMessage; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.messages.Messages; import org.keycloak.services.resources.AttributeFormDataProcessor; import org.keycloak.services.validation.Validation; @@ -43,7 +42,6 @@ import java.util.List; * @version $Revision: 1 $ */ public class UpdateProfile implements RequiredActionProvider, RequiredActionFactory { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; @Override public void evaluateTriggers(RequiredActionContext context) { } diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java index 62344bdf5a..829c705f6d 100644 --- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java +++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java @@ -28,7 +28,6 @@ import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; import org.keycloak.models.utils.CredentialValidation; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.messages.Messages; import org.keycloak.services.validation.Validation; @@ -40,7 +39,6 @@ import javax.ws.rs.core.Response; * @version $Revision: 1 $ */ public class UpdateTotp implements RequiredActionProvider, RequiredActionFactory { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; @Override public void evaluateTriggers(RequiredActionContext context) { } diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java b/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java index 0888113d6b..2d683d3afa 100755 --- a/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java +++ b/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java @@ -17,6 +17,7 @@ package org.keycloak.authentication.requiredactions; +import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.authentication.RequiredActionContext; import org.keycloak.authentication.RequiredActionFactory; @@ -41,7 +42,7 @@ import javax.ws.rs.core.Response; * @version $Revision: 1 $ */ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactory { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(VerifyEmail.class); @Override public void evaluateTriggers(RequiredActionContext context) { if (context.getRealm().isVerifyEmail() && !context.getUser().isEmailVerified()) { diff --git a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java index fcb41a4dca..346cf82da3 100755 --- a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java +++ b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java @@ -17,21 +17,13 @@ package org.keycloak.broker.oidc; import org.keycloak.broker.provider.AbstractIdentityProviderFactory; -import org.keycloak.jose.jwk.JSONWebKeySet; -import org.keycloak.jose.jwk.JWK; -import org.keycloak.jose.jwk.JWKParser; import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation; -import org.keycloak.protocol.oidc.utils.JWKSHttpUtils; -import org.keycloak.services.ServicesLogger; -import org.keycloak.util.JWKSUtils; import org.keycloak.util.JsonSerialization; import java.io.IOException; import java.io.InputStream; -import java.security.PublicKey; import java.util.Map; /** @@ -39,8 +31,6 @@ import java.util.Map; */ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; - public static final String PROVIDER_ID = "oidc"; @Override diff --git a/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java b/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java index f94fe07485..235f123445 100644 --- a/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java +++ b/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java @@ -17,6 +17,7 @@ package org.keycloak.email; +import org.jboss.logging.Logger; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; @@ -44,7 +45,7 @@ import java.util.Properties; */ public class DefaultEmailSenderProvider implements EmailSenderProvider { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(DefaultEmailSenderProvider.class); private final KeycloakSession session; @@ -123,7 +124,7 @@ public class DefaultEmailSenderProvider implements EmailSenderProvider { } transport.sendMessage(msg, new InternetAddress[]{new InternetAddress(address)}); } catch (Exception e) { - logger.failedToSendEmail(e); + ServicesLogger.LOGGER.failedToSendEmail(e); throw new EmailException(e); } finally { if (transport != null) { diff --git a/services/src/main/java/org/keycloak/exportimport/ExportImportManager.java b/services/src/main/java/org/keycloak/exportimport/ExportImportManager.java index b51513cb59..3e2188e549 100644 --- a/services/src/main/java/org/keycloak/exportimport/ExportImportManager.java +++ b/services/src/main/java/org/keycloak/exportimport/ExportImportManager.java @@ -18,6 +18,7 @@ package org.keycloak.exportimport; +import org.jboss.logging.Logger; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.services.ServicesLogger; @@ -29,7 +30,7 @@ import java.io.IOException; */ public class ExportImportManager { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(ExportImportManager.class); private KeycloakSessionFactory sessionFactory; @@ -82,13 +83,13 @@ public class ExportImportManager { try { Strategy strategy = ExportImportConfig.getStrategy(); if (realmName == null) { - logger.fullModelImport(strategy.toString()); + ServicesLogger.LOGGER.fullModelImport(strategy.toString()); importProvider.importModel(sessionFactory, strategy); } else { - logger.realmImportRequested(realmName, strategy.toString()); + ServicesLogger.LOGGER.realmImportRequested(realmName, strategy.toString()); importProvider.importRealm(sessionFactory, realmName, strategy); } - logger.importSuccess(); + ServicesLogger.LOGGER.importSuccess(); } catch (IOException e) { throw new RuntimeException("Failed to run import", e); } @@ -97,13 +98,13 @@ public class ExportImportManager { public void runExport() { try { if (realmName == null) { - logger.fullModelExportRequested(); + ServicesLogger.LOGGER.fullModelExportRequested(); exportProvider.exportModel(sessionFactory); } else { - logger.realmExportRequested(realmName); + ServicesLogger.LOGGER.realmExportRequested(realmName); exportProvider.exportRealm(sessionFactory, realmName); } - logger.exportSuccess(); + ServicesLogger.LOGGER.exportSuccess(); } catch (IOException e) { throw new RuntimeException("Failed to run export"); } diff --git a/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProviderFactory.java b/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProviderFactory.java index 069160758b..518d321783 100644 --- a/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProviderFactory.java +++ b/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProviderFactory.java @@ -43,10 +43,10 @@ public class JavaKeystoreKeyProviderFactory extends AbstractRsaKeyProviderFactor public static ProviderConfigProperty KEYSTORE_PASSWORD_PROPERTY = new ProviderConfigProperty(KEYSTORE_PASSWORD_KEY, "Keystore Password", "Password for the keys", STRING_TYPE, null, true); public static String KEY_ALIAS_KEY = "keyAlias"; - public static ProviderConfigProperty KEY_ALIAS_PROPERTY = new ProviderConfigProperty(KEY_ALIAS_KEY, "Private Key Alias", "Alias for the private key", STRING_TYPE, null); + public static ProviderConfigProperty KEY_ALIAS_PROPERTY = new ProviderConfigProperty(KEY_ALIAS_KEY, "Key Alias", "Alias for the private key", STRING_TYPE, null); public static String KEY_PASSWORD_KEY = "keyPassword"; - public static ProviderConfigProperty KEY_PASSWORD_PROPERTY = new ProviderConfigProperty(KEY_PASSWORD_KEY, "Private Key password", "Password for the private key", STRING_TYPE, null, true); + public static ProviderConfigProperty KEY_PASSWORD_PROPERTY = new ProviderConfigProperty(KEY_PASSWORD_KEY, "Key Password", "Password for the private key", STRING_TYPE, null, true); private static final String HELP_TEXT = "Loads keys from a Java keys file"; diff --git a/services/src/main/java/org/keycloak/keys/loader/ClientPublicKeyLoader.java b/services/src/main/java/org/keycloak/keys/loader/ClientPublicKeyLoader.java index 23eb726985..d3f7fe5823 100644 --- a/services/src/main/java/org/keycloak/keys/loader/ClientPublicKeyLoader.java +++ b/services/src/main/java/org/keycloak/keys/loader/ClientPublicKeyLoader.java @@ -22,6 +22,7 @@ import java.security.cert.X509Certificate; import java.util.Collections; import java.util.Map; +import org.jboss.logging.Logger; import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator; import org.keycloak.common.util.KeyUtils; import org.keycloak.jose.jwk.JSONWebKeySet; @@ -44,7 +45,7 @@ import org.keycloak.util.JWKSUtils; */ public class ClientPublicKeyLoader implements PublicKeyLoader { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(ClientPublicKeyLoader.class); private final KeycloakSession session; private final ClientModel client; diff --git a/services/src/main/java/org/keycloak/keys/loader/OIDCIdentityProviderPublicKeyLoader.java b/services/src/main/java/org/keycloak/keys/loader/OIDCIdentityProviderPublicKeyLoader.java index f41ded8196..cf24a1be3e 100644 --- a/services/src/main/java/org/keycloak/keys/loader/OIDCIdentityProviderPublicKeyLoader.java +++ b/services/src/main/java/org/keycloak/keys/loader/OIDCIdentityProviderPublicKeyLoader.java @@ -17,10 +17,7 @@ package org.keycloak.keys.loader; -import java.security.PublicKey; -import java.util.Collections; -import java.util.Map; - +import org.jboss.logging.Logger; import org.keycloak.broker.oidc.OIDCIdentityProviderConfig; import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.PemUtils; @@ -29,15 +26,18 @@ import org.keycloak.jose.jwk.JWK; import org.keycloak.keys.PublicKeyLoader; import org.keycloak.models.KeycloakSession; import org.keycloak.protocol.oidc.utils.JWKSHttpUtils; -import org.keycloak.services.ServicesLogger; import org.keycloak.util.JWKSUtils; +import java.security.PublicKey; +import java.util.Collections; +import java.util.Map; + /** * @author Marek Posolda */ public class OIDCIdentityProviderPublicKeyLoader implements PublicKeyLoader { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(OIDCIdentityProviderPublicKeyLoader.class); private final KeycloakSession session; private final OIDCIdentityProviderConfig config; diff --git a/services/src/main/java/org/keycloak/partialimport/AbstractPartialImport.java b/services/src/main/java/org/keycloak/partialimport/AbstractPartialImport.java index cd81c560a5..ec42600f2a 100644 --- a/services/src/main/java/org/keycloak/partialimport/AbstractPartialImport.java +++ b/services/src/main/java/org/keycloak/partialimport/AbstractPartialImport.java @@ -34,7 +34,6 @@ import java.util.Set; * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc. */ public abstract class AbstractPartialImport implements PartialImport { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; protected final Set toOverwrite = new HashSet<>(); protected final Set toSkip = new HashSet<>(); @@ -100,7 +99,7 @@ public abstract class AbstractPartialImport implements PartialImport { try { create(realm, session, resourceRep); } catch (Exception e) { - logger.overwriteError(e, getName(resourceRep)); + ServicesLogger.LOGGER.overwriteError(e, getName(resourceRep)); throw new ErrorResponseException(ErrorResponse.error(e.getMessage(), Response.Status.INTERNAL_SERVER_ERROR)); } @@ -122,7 +121,7 @@ public abstract class AbstractPartialImport implements PartialImport { String modelId = getModelId(realm, session, resourceRep); results.addResult(added(modelId, resourceRep)); } catch (Exception e) { - logger.creationError(e, getName(resourceRep)); + ServicesLogger.LOGGER.creationError(e, getName(resourceRep)); throw new ErrorResponseException(ErrorResponse.error(e.getMessage(), Response.Status.INTERNAL_SERVER_ERROR)); } } diff --git a/services/src/main/java/org/keycloak/partialimport/RolesPartialImport.java b/services/src/main/java/org/keycloak/partialimport/RolesPartialImport.java index 6fca177a05..c4b295bfb5 100644 --- a/services/src/main/java/org/keycloak/partialimport/RolesPartialImport.java +++ b/services/src/main/java/org/keycloak/partialimport/RolesPartialImport.java @@ -45,7 +45,6 @@ import java.util.Set; * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc. */ public class RolesPartialImport implements PartialImport { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; private Set realmRolesToOverwrite; private Set realmRolesToSkip; @@ -98,7 +97,7 @@ public class RolesPartialImport implements PartialImport { try { RepresentationToModel.importRoles(rep.getRoles(), realm); } catch (Exception e) { - logger.roleImportError(e); + ServicesLogger.LOGGER.roleImportError(e); throw new ErrorResponseException(ErrorResponse.error(e.getMessage(), Response.Status.INTERNAL_SERVER_ERROR)); } diff --git a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java index c1a101c014..0c1462c787 100755 --- a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java +++ b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java @@ -43,8 +43,6 @@ import javax.ws.rs.core.UriInfo; */ public abstract class AuthorizationEndpointBase { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; - protected RealmModel realm; protected EventBuilder event; protected AuthenticationManager authManager; diff --git a/services/src/main/java/org/keycloak/protocol/RestartLoginCookie.java b/services/src/main/java/org/keycloak/protocol/RestartLoginCookie.java index cc7c3244a1..b333afa614 100644 --- a/services/src/main/java/org/keycloak/protocol/RestartLoginCookie.java +++ b/services/src/main/java/org/keycloak/protocol/RestartLoginCookie.java @@ -18,21 +18,19 @@ package org.keycloak.protocol; import com.fasterxml.jackson.annotation.JsonProperty; +import org.jboss.logging.Logger; import org.keycloak.common.ClientConnection; import org.keycloak.jose.jws.JWSBuilder; import org.keycloak.jose.jws.JWSInput; -import org.keycloak.jose.jws.crypto.HMACProvider; import org.keycloak.jose.jws.crypto.RSAProvider; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientSessionModel; import org.keycloak.models.KeyManager; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.util.CookieHelper; -import javax.crypto.SecretKey; import javax.ws.rs.core.Cookie; import javax.ws.rs.core.UriInfo; import java.security.PublicKey; @@ -47,7 +45,7 @@ import java.util.Map; * @version $Revision: 1 $ */ public class RestartLoginCookie { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(RestartLoginCookie.class); public static final String KC_RESTART = "KC_RESTART"; @JsonProperty("cs") protected String clientSession; diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java index c263405213..7e4141195e 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java @@ -16,6 +16,7 @@ */ package org.keycloak.protocol.oidc; +import org.jboss.logging.Logger; import org.keycloak.OAuth2Constants; import org.keycloak.OAuthErrorException; import org.keycloak.common.util.Time; @@ -84,7 +85,7 @@ public class OIDCLoginProtocol implements LoginProtocol { public static final String CLIENT_SECRET_JWT = "client_secret_jwt"; public static final String PRIVATE_KEY_JWT = "private_key_jwt"; - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(OIDCLoginProtocol.class); protected KeycloakSession session; @@ -233,7 +234,7 @@ public class OIDCLoginProtocol implements LoginProtocol { case PASSIVE_LOGIN_REQUIRED: return OAuthErrorException.LOGIN_REQUIRED; default: - logger.untranslatedProtocol(error.name()); + ServicesLogger.LOGGER.untranslatedProtocol(error.name()); return OAuthErrorException.SERVER_ERROR; } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java index d830fcdb09..03328a0e44 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java @@ -16,6 +16,7 @@ */ package org.keycloak.protocol.oidc; +import org.jboss.logging.Logger; import org.keycloak.common.constants.KerberosConstants; import org.keycloak.common.util.UriUtils; import org.keycloak.events.EventBuilder; @@ -48,7 +49,7 @@ import java.util.Set; * @version $Revision: 1 $ */ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory { - private static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(OIDCLoginProtocolFactory.class); public static final String USERNAME = "username"; public static final String EMAIL = "email"; @@ -193,7 +194,7 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory { // Backwards compatibility only if (rep.isDirectGrantsOnly() != null) { - logger.usingDeprecatedDirectGrantsOnly(); + ServicesLogger.LOGGER.usingDeprecatedDirectGrantsOnly(); newClient.setStandardFlowEnabled(!rep.isDirectGrantsOnly()); newClient.setDirectAccessGrantsEnabled(rep.isDirectGrantsOnly()); } else { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java index a07ea709be..2c983edf3d 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java @@ -20,9 +20,10 @@ package org.keycloak.protocol.oidc; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.events.EventBuilder; +import org.keycloak.forms.login.LoginFormsProvider; +import org.keycloak.jose.jwk.JSONWebKeySet; import org.keycloak.jose.jwk.JWK; import org.keycloak.jose.jwk.JWKBuilder; -import org.keycloak.forms.login.LoginFormsProvider; import org.keycloak.keys.KeyMetadata; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; @@ -31,8 +32,6 @@ import org.keycloak.protocol.oidc.endpoints.LoginStatusIframeEndpoint; import org.keycloak.protocol.oidc.endpoints.LogoutEndpoint; import org.keycloak.protocol.oidc.endpoints.TokenEndpoint; import org.keycloak.protocol.oidc.endpoints.UserInfoEndpoint; -import org.keycloak.jose.jwk.JSONWebKeySet; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.resources.RealmsResource; import javax.ws.rs.GET; @@ -55,8 +54,6 @@ import java.util.List; */ public class OIDCLoginProtocolService { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; - private RealmModel realm; private TokenManager tokenManager; private EventBuilder event; diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java index 308c771d79..c67160794b 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java @@ -506,10 +506,11 @@ public class TokenManager { for (ProtocolMapperModel mapping : mappings) { ProtocolMapper mapper = (ProtocolMapper)sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper()); - if (mapper == null || !(mapper instanceof OIDCAccessTokenMapper)) continue; - token = ((OIDCAccessTokenMapper)mapper).transformAccessToken(token, mapping, session, userSession, clientSession); - + if (mapper instanceof OIDCAccessTokenMapper) { + token = ((OIDCAccessTokenMapper) mapper).transformAccessToken(token, mapping, session, userSession, clientSession); + } } + return token; } @@ -520,16 +521,11 @@ public class TokenManager { for (ProtocolMapperModel mapping : mappings) { ProtocolMapper mapper = (ProtocolMapper)sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper()); - if (mapper == null || !(mapper instanceof OIDCAccessTokenMapper)) continue; - - if(mapper instanceof UserInfoTokenMapper){ - token = ((UserInfoTokenMapper)mapper).transformUserInfoToken(token, mapping, session, userSession, clientSession); - continue; + if (mapper instanceof UserInfoTokenMapper) { + token = ((UserInfoTokenMapper) mapper).transformUserInfoToken(token, mapping, session, userSession, clientSession); } - - token = ((OIDCAccessTokenMapper)mapper).transformAccessToken(token, mapping, session, userSession, clientSession); - } + return token; } @@ -540,13 +536,12 @@ public class TokenManager { for (ProtocolMapperModel mapping : mappings) { ProtocolMapper mapper = (ProtocolMapper)sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper()); - if (mapper == null || !(mapper instanceof OIDCIDTokenMapper)) continue; - token = ((OIDCIDTokenMapper)mapper).transformIDToken(token, mapping, session, userSession, clientSession); - + if (mapper instanceof OIDCIDTokenMapper) { + token = ((OIDCIDTokenMapper) mapper).transformIDToken(token, mapping, session, userSession, clientSession); + } } } - protected AccessToken initToken(RealmModel realm, ClientModel client, UserModel user, UserSessionModel session, ClientSessionModel clientSession, UriInfo uriInfo) { AccessToken token = new AccessToken(); if (clientSession != null) token.clientSession(clientSession.getId()); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java index 4ef0c5cc56..b979063d81 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java @@ -17,6 +17,7 @@ package org.keycloak.protocol.oidc.endpoints; +import org.jboss.logging.Logger; import org.keycloak.OAuth2Constants; import org.keycloak.OAuthErrorException; import org.keycloak.authentication.AuthenticationProcessor; @@ -54,7 +55,7 @@ import javax.ws.rs.core.Response; */ public class AuthorizationEndpoint extends AuthorizationEndpointBase { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(AuthorizationEndpoint.class); public static final String CODE_AUTH_TYPE = "code"; @@ -104,7 +105,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase { } if (!TokenUtil.isOIDCRequest(request.getScope())) { - logger.oidcScopeMissing(); + ServicesLogger.LOGGER.oidcScopeMissing(); } errorResponse = checkOIDCParams(); @@ -194,7 +195,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase { String responseType = request.getResponseType(); if (responseType == null) { - logger.missingParameter(OAuth2Constants.RESPONSE_TYPE); + ServicesLogger.LOGGER.missingParameter(OAuth2Constants.RESPONSE_TYPE); event.error(Errors.INVALID_REQUEST); return redirectErrorToClient(OIDCResponseMode.QUERY, OAuthErrorException.INVALID_REQUEST, "Missing parameter: response_type"); } @@ -216,7 +217,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase { try { parsedResponseMode = OIDCResponseMode.parse(request.getResponseMode(), parsedResponseType); } catch (IllegalArgumentException iae) { - logger.invalidParameter(OIDCLoginProtocol.RESPONSE_MODE_PARAM); + ServicesLogger.LOGGER.invalidParameter(OIDCLoginProtocol.RESPONSE_MODE_PARAM); event.error(Errors.INVALID_REQUEST); return redirectErrorToClient(OIDCResponseMode.QUERY, OAuthErrorException.INVALID_REQUEST, "Invalid parameter: response_mode"); } @@ -225,19 +226,19 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase { // Disallowed by OIDC specs if (parsedResponseType.isImplicitOrHybridFlow() && parsedResponseMode == OIDCResponseMode.QUERY) { - logger.responseModeQueryNotAllowed(); + ServicesLogger.LOGGER.responseModeQueryNotAllowed(); event.error(Errors.INVALID_REQUEST); return redirectErrorToClient(OIDCResponseMode.QUERY, OAuthErrorException.INVALID_REQUEST, "Response_mode 'query' not allowed for implicit or hybrid flow"); } if ((parsedResponseType.hasResponseType(OIDCResponseType.CODE) || parsedResponseType.hasResponseType(OIDCResponseType.NONE)) && !client.isStandardFlowEnabled()) { - logger.flowNotAllowed("Standard"); + ServicesLogger.LOGGER.flowNotAllowed("Standard"); event.error(Errors.NOT_ALLOWED); return redirectErrorToClient(parsedResponseMode, OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE, "Client is not allowed to initiate browser login with given response_type. Standard flow is disabled for the client."); } if (parsedResponseType.isImplicitOrHybridFlow() && !client.isImplicitFlowEnabled()) { - logger.flowNotAllowed("Implicit"); + ServicesLogger.LOGGER.flowNotAllowed("Implicit"); event.error(Errors.NOT_ALLOWED); return redirectErrorToClient(parsedResponseMode, OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE, "Client is not allowed to initiate browser login with given response_type. Implicit flow is disabled for the client."); } @@ -249,7 +250,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase { private Response checkOIDCParams() { if (parsedResponseType.isImplicitOrHybridFlow() && request.getNonce() == null) { - logger.missingParameter(OIDCLoginProtocol.NONCE_PARAM); + ServicesLogger.LOGGER.missingParameter(OIDCLoginProtocol.NONCE_PARAM); event.error(Errors.INVALID_REQUEST); return redirectErrorToClient(parsedResponseMode, OAuthErrorException.INVALID_REQUEST, "Missing parameter: nonce"); } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java index 44822c6b62..0c17664130 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java @@ -17,6 +17,7 @@ package org.keycloak.protocol.oidc.endpoints; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.OAuth2Constants; @@ -38,7 +39,6 @@ import org.keycloak.representations.IDToken; import org.keycloak.representations.RefreshToken; import org.keycloak.services.ErrorPage; import org.keycloak.services.ErrorResponseException; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.messages.Messages; import org.keycloak.services.resources.Cors; @@ -60,7 +60,7 @@ import javax.ws.rs.core.UriInfo; * @author Stian Thorgersen */ public class LogoutEndpoint { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(LogoutEndpoint.class); @Context private KeycloakSession session; diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java index 9a7fe3ef19..0a4803c92b 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java @@ -17,6 +17,7 @@ package org.keycloak.protocol.oidc.endpoints; +import org.jboss.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.OAuth2Constants; @@ -68,7 +69,7 @@ import java.util.Map; */ public class TokenEndpoint { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(TokenEndpoint.class); private MultivaluedMap formParams; private ClientModel client; private Map clientAuthAttributes; @@ -309,7 +310,7 @@ public class TokenEndpoint { private void updateClientSession(ClientSessionModel clientSession) { if(clientSession == null) { - logger.clientSessionNull(); + ServicesLogger.LOGGER.clientSessionNull(); return; } @@ -327,16 +328,16 @@ public class TokenEndpoint { private void updateClientSessions(List clientSessions) { if(clientSessions == null) { - logger.clientSessionNull(); + ServicesLogger.LOGGER.clientSessionNull(); return; } for (ClientSessionModel clientSession : clientSessions) { if(clientSession == null) { - logger.clientSessionNull(); + ServicesLogger.LOGGER.clientSessionNull(); continue; } if(clientSession.getClient() == null) { - logger.clientModelNull(); + ServicesLogger.LOGGER.clientModelNull(); continue; } if(client.getId().equals(clientSession.getClient().getId())) { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequestParserProcessor.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequestParserProcessor.java index 4469c0f0db..c4de3131c6 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequestParserProcessor.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequestParserProcessor.java @@ -36,8 +36,6 @@ import java.io.InputStream; */ public class AuthorizationEndpointRequestParserProcessor { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; - public static AuthorizationEndpointRequest parseRequest(EventBuilder event, KeycloakSession session, ClientModel client, MultivaluedMap requestParams) { try { AuthorizationEndpointRequest request = new AuthorizationEndpointRequest(); @@ -63,7 +61,7 @@ public class AuthorizationEndpointRequestParserProcessor { return request; } catch (Exception e) { - logger.invalidRequest(e); + ServicesLogger.LOGGER.invalidRequest(e); event.error(Errors.INVALID_REQUEST); throw new ErrorPageException(session, Messages.INVALID_REQUEST); } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java index 82d2c955ca..ea1c35ed0c 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java @@ -17,9 +17,9 @@ package org.keycloak.protocol.oidc.endpoints.request; +import org.jboss.logging.Logger; import org.keycloak.constants.AdapterConstants; import org.keycloak.protocol.oidc.OIDCLoginProtocol; -import org.keycloak.services.ServicesLogger; import java.util.HashSet; import java.util.Map; @@ -30,7 +30,7 @@ import java.util.Set; */ abstract class AuthzEndpointRequestParser { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(AuthzEndpointRequestParser.class); /** * Max number of additional req params copied into client session note to prevent DoS attacks diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractOIDCProtocolMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractOIDCProtocolMapper.java index 7c5b3b9a9d..efe9434b84 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractOIDCProtocolMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractOIDCProtocolMapper.java @@ -18,10 +18,15 @@ package org.keycloak.protocol.oidc.mappers; import org.keycloak.Config; +import org.keycloak.models.ClientSessionModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.ProtocolMapper; import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.representations.AccessToken; +import org.keycloak.representations.IDToken; /** * @author Bill Burke @@ -54,4 +59,46 @@ public abstract class AbstractOIDCProtocolMapper implements ProtocolMapper { public void postInit(KeycloakSessionFactory factory) { } + + public AccessToken transformUserInfoToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, + UserSessionModel userSession, ClientSessionModel clientSession) { + + if (!OIDCAttributeMapperHelper.includeInUserInfo(mappingModel)) { + return token; + } + + setClaim(token, mappingModel, userSession); + return token; + } + + public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, + UserSessionModel userSession, ClientSessionModel clientSession) { + + if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)){ + return token; + } + + setClaim(token, mappingModel, userSession); + return token; + } + + public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, + UserSessionModel userSession, ClientSessionModel clientSession) { + + if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)){ + return token; + } + + setClaim(token, mappingModel, userSession); + return token; + } + + /** + * Intended to be overridden in {@link ProtocolMapper} implementations to add claims to an token. + * @param token + * @param mappingModel + * @param userSession + */ + protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) { + } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractUserRoleMappingMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractUserRoleMappingMapper.java index 3e639aab5b..de4d0548b3 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractUserRoleMappingMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractUserRoleMappingMapper.java @@ -35,33 +35,7 @@ import java.util.Set; * * @author Thomas Darimont */ -abstract class AbstractUserRoleMappingMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { - - @Override - public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, - UserSessionModel userSession, ClientSessionModel clientSession) { - - if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) { - return token; - } - - setClaim(token, mappingModel, userSession); - return token; - } - - @Override - public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { - - if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) { - return token; - } - - setClaim(token, mappingModel, userSession); - return token; - } - - - protected abstract void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession); +abstract class AbstractUserRoleMappingMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { /** * Returns the role names extracted from the given {@code roleModels} while recursively traversing "Composite Roles". diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java index 4f5d9cf096..674c9ff10c 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java @@ -39,26 +39,12 @@ import java.util.Map; * @author Bill Burke * @version $Revision: 1 $ */ -public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { +public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { private static final List configProperties = new ArrayList(); static { - ProviderConfigProperty property; - property = new ProviderConfigProperty(); - property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN); - property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL); - property.setType(ProviderConfigProperty.BOOLEAN_TYPE); - property.setDefaultValue("true"); - property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT); - configProperties.add(property); - property = new ProviderConfigProperty(); - property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN); - property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL); - property.setType(ProviderConfigProperty.BOOLEAN_TYPE); - property.setDefaultValue("true"); - property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT); - configProperties.add(property); + OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, AddressMapper.class); } public static final String PROVIDER_ID = "oidc-address-mapper"; @@ -118,21 +104,7 @@ public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAcc } @Override - public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, - UserSessionModel userSession, ClientSessionModel clientSession) { - if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) return token; - setClaim(token, userSession); - return token; - } - - @Override - public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { - if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) return token; - setClaim(token, userSession); - return token; - } - - protected void setClaim(IDToken token, UserSessionModel userSession) { + protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) { UserModel user = userSession.getUser(); AddressClaimSet addressSet = new AddressClaimSet(); addressSet.setStreetAddress(user.getFirstAttribute("street")); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java index 107e163ffb..1e4ad9df09 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java @@ -38,26 +38,12 @@ import java.util.Map; * @author Bill Burke * @version $Revision: 1 $ */ -public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { +public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { private static final List configProperties = new ArrayList(); static { - ProviderConfigProperty property; - property = new ProviderConfigProperty(); - property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN); - property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL); - property.setType(ProviderConfigProperty.BOOLEAN_TYPE); - property.setDefaultValue("true"); - property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT); - configProperties.add(property); - property = new ProviderConfigProperty(); - property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN); - property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL); - property.setType(ProviderConfigProperty.BOOLEAN_TYPE); - property.setDefaultValue("true"); - property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT); - configProperties.add(property); + OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, FullNameMapper.class); } @@ -88,28 +74,13 @@ public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc return "Maps the user's first and last name to the OpenID Connect 'name' claim. Format is + ' ' + "; } - @Override - public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, - UserSessionModel userSession, ClientSessionModel clientSession) { - if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) return token; - setClaim(token, userSession); - return token; - } - - protected void setClaim(IDToken token, UserSessionModel userSession) { + protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) { UserModel user = userSession.getUser(); String first = user.getFirstName() == null ? "" : user.getFirstName() + " "; String last = user.getLastName() == null ? "" : user.getLastName(); token.getOtherClaims().put("name", first + last); } - @Override - public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { - if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) return token; - setClaim(token, userSession); - return token; - } - public static ProtocolMapperModel create(String name, boolean consentRequired, String consentText, boolean accessToken, boolean idToken) { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/GroupMembershipMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/GroupMembershipMapper.java index d1410c34aa..41dbb47db9 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/GroupMembershipMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/GroupMembershipMapper.java @@ -40,21 +40,13 @@ import java.util.Map; * @author Bill Burke * @version $Revision: 1 $ */ -public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { +public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { private static final List configProperties = new ArrayList(); static { - ProviderConfigProperty property; - ProviderConfigProperty property1; - property1 = new ProviderConfigProperty(); - property1.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME); - property1.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_LABEL); - property1.setType(ProviderConfigProperty.STRING_TYPE); - property1.setDefaultValue("groups"); - property1.setHelpText(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_TOOLTIP); - configProperties.add(property1); - property1 = new ProviderConfigProperty(); + OIDCAttributeMapperHelper.addTokenClaimNameConfig(configProperties); + ProviderConfigProperty property1 = new ProviderConfigProperty(); property1.setName("full.path"); property1.setLabel("Full group path"); property1.setType(ProviderConfigProperty.BOOLEAN_TYPE); @@ -62,23 +54,7 @@ public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements property1.setHelpText("Include full path to group i.e. /top/level1/level2, false will just specify the group name"); configProperties.add(property1); - property1 = new ProviderConfigProperty(); - property1.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN); - property1.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL); - property1.setType(ProviderConfigProperty.BOOLEAN_TYPE); - property1.setDefaultValue("true"); - property1.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT); - configProperties.add(property1); - property1 = new ProviderConfigProperty(); - property1.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN); - property1.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL); - property1.setType(ProviderConfigProperty.BOOLEAN_TYPE); - property1.setDefaultValue("true"); - property1.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT); - configProperties.add(property1); - - - + OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, GroupMembershipMapper.class); } public static final String PROVIDER_ID = "oidc-group-membership-mapper"; @@ -113,15 +89,14 @@ public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements } - @Override - public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, - UserSessionModel userSession, ClientSessionModel clientSession) { - if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) return token; - buildMembership(token, mappingModel, userSession); - return token; - } + /** + * Adds the group membership information to the {@link IDToken#otherClaims}. + * @param token + * @param mappingModel + * @param userSession + */ + protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) { - public void buildMembership(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) { List membership = new LinkedList<>(); boolean fullPath = useFullPath(mappingModel); for (GroupModel group : userSession.getUser().getGroups()) { @@ -136,13 +111,6 @@ public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements token.getOtherClaims().put(protocolClaim, membership); } - @Override - public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { - if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) return token; - buildMembership(token, mappingModel, userSession); - return token; - } - public static ProtocolMapperModel create(String name, String tokenClaimName, boolean consentRequired, String consentText, diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java index f5015bab3f..40628245dd 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java @@ -37,53 +37,24 @@ import java.util.Map; * @author Bill Burke * @version $Revision: 1 $ */ -public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { +public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { private static final List configProperties = new ArrayList(); public static final String CLAIM_VALUE = "claim.value"; static { - ProviderConfigProperty property; - property = new ProviderConfigProperty(); - property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME); - property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_LABEL); - property.setType(ProviderConfigProperty.STRING_TYPE); - property.setHelpText("Claim name you want to hard code into the token. This can be a fully qualified name like 'address.street'. In this case, a nested json object will be created."); - configProperties.add(property); - property = new ProviderConfigProperty(); + OIDCAttributeMapperHelper.addTokenClaimNameConfig(configProperties); + + ProviderConfigProperty property = new ProviderConfigProperty(); property.setName(CLAIM_VALUE); property.setLabel("Claim value"); property.setType(ProviderConfigProperty.STRING_TYPE); property.setHelpText("Value of the claim you want to hard code. 'true' and 'false can be used for boolean values."); configProperties.add(property); - property = new ProviderConfigProperty(); - property.setName(OIDCAttributeMapperHelper.JSON_TYPE); - property.setLabel(OIDCAttributeMapperHelper.JSON_TYPE); - List types = new ArrayList(3); - types.add("String"); - types.add("long"); - types.add("int"); - types.add("boolean"); - property.setType(ProviderConfigProperty.LIST_TYPE); - property.setDefaultValue(types); - property.setHelpText("JSON type that should be used for the value of the claim. long, int, boolean, and String are valid values."); - configProperties.add(property); - property = new ProviderConfigProperty(); - property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN); - property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL); - property.setType(ProviderConfigProperty.BOOLEAN_TYPE); - property.setDefaultValue("true"); - property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT); - configProperties.add(property); - property = new ProviderConfigProperty(); - property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN); - property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL); - property.setType(ProviderConfigProperty.BOOLEAN_TYPE); - property.setDefaultValue("true"); - property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT); - configProperties.add(property); + OIDCAttributeMapperHelper.addJsonTypeConfig(configProperties); + OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, HardcodedClaim.class); } public static final String PROVIDER_ID = "oidc-hardcoded-claim-mapper"; @@ -113,28 +84,13 @@ public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAc return "Hardcode a claim into the token."; } - @Override - public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, - UserSessionModel userSession, ClientSessionModel clientSession) { - if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) return token; - - setClaim(token, mappingModel, userSession); - return token; - } - protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) { + String attributeValue = mappingModel.getConfig().get(CLAIM_VALUE); if (attributeValue == null) return; OIDCAttributeMapperHelper.mapClaim(token, mappingModel, attributeValue); } - @Override - public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { - if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) return token; - setClaim(token, mappingModel, userSession); - return token; - } - public static ProtocolMapperModel create(String name, String hardcodedName, String hardcodedValue, String claimType, diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java index 55298579fe..03ecb91eb6 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java @@ -83,6 +83,7 @@ public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAcc @Override public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { + String role = mappingModel.getConfig().get(ROLE_CONFIG); String[] scopedRole = KeycloakModelUtils.parseRole(role); String appName = scopedRole[0]; @@ -97,6 +98,7 @@ public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAcc } access.addRole(role); } + return token; } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java index 0c05aa190d..99b261074b 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java @@ -18,6 +18,7 @@ package org.keycloak.protocol.oidc.mappers; import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.protocol.ProtocolMapper; import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.provider.ProviderConfigProperty; @@ -34,7 +35,6 @@ import java.util.Map; * @version $Revision: 1 $ */ public class OIDCAttributeMapperHelper { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; public static final String TOKEN_CLAIM_NAME = "claim.name"; public static final String TOKEN_CLAIM_NAME_LABEL = "tokenClaimName.label"; @@ -67,7 +67,7 @@ public class OIDCAttributeMapperHelper { return result; } else { if (valueAsList.size() > 1) { - logger.multipleValuesForMapper(attributeValue.toString(), mappingModel.getName()); + ServicesLogger.LOGGER.multipleValuesForMapper(attributeValue.toString(), mappingModel.getName()); } attributeValue = valueAsList.get(0); @@ -124,7 +124,7 @@ public class OIDCAttributeMapperHelper { boolean consentRequired, String consentText, boolean accessToken, boolean idToken, String mapperId) { - return createClaimMapper(name, userAttribute,tokenClaimName, claimType, consentRequired, consentText, accessToken, idToken, false, mapperId); + return createClaimMapper(name, userAttribute,tokenClaimName, claimType, consentRequired, consentText, accessToken, idToken, true, mapperId); } public static ProtocolMapperModel createClaimMapper(String name, @@ -163,18 +163,34 @@ public class OIDCAttributeMapperHelper { } public static boolean includeInUserInfo(ProtocolMapperModel mappingModel){ - return "true".equals(mappingModel.getConfig().get(INCLUDE_IN_USERINFO)); + String includeInUserInfo = mappingModel.getConfig().get(INCLUDE_IN_USERINFO); + + // Backwards compatibility + if (includeInUserInfo == null && includeInIDToken(mappingModel)) { + return true; + } + + return "true".equals(includeInUserInfo); } - public static void addAttributeConfig(List configProperties) { - ProviderConfigProperty property; - property = new ProviderConfigProperty(); + public static void addAttributeConfig(List configProperties, Class protocolMapperClass) { + addTokenClaimNameConfig(configProperties); + addJsonTypeConfig(configProperties); + + addIncludeInTokensConfig(configProperties, protocolMapperClass); + } + + public static void addTokenClaimNameConfig(List configProperties) { + ProviderConfigProperty property = new ProviderConfigProperty(); property.setName(TOKEN_CLAIM_NAME); property.setLabel(TOKEN_CLAIM_NAME_LABEL); property.setType(ProviderConfigProperty.STRING_TYPE); property.setHelpText(TOKEN_CLAIM_NAME_TOOLTIP); configProperties.add(property); - property = new ProviderConfigProperty(); + } + + public static void addJsonTypeConfig(List configProperties) { + ProviderConfigProperty property = new ProviderConfigProperty(); property.setName(JSON_TYPE); property.setLabel(JSON_TYPE); List types = new ArrayList(3); @@ -183,29 +199,40 @@ public class OIDCAttributeMapperHelper { types.add("int"); types.add("boolean"); property.setType(ProviderConfigProperty.LIST_TYPE); - property.setDefaultValue(types); + property.setOptions(types); property.setHelpText(JSON_TYPE_TOOLTIP); configProperties.add(property); - property = new ProviderConfigProperty(); - property.setName(INCLUDE_IN_ID_TOKEN); - property.setLabel(INCLUDE_IN_ID_TOKEN_LABEL); - property.setType(ProviderConfigProperty.BOOLEAN_TYPE); - property.setDefaultValue("true"); - property.setHelpText(INCLUDE_IN_ID_TOKEN_HELP_TEXT); - configProperties.add(property); - property = new ProviderConfigProperty(); - property.setName(INCLUDE_IN_ACCESS_TOKEN); - property.setLabel(INCLUDE_IN_ACCESS_TOKEN_LABEL); - property.setType(ProviderConfigProperty.BOOLEAN_TYPE); - property.setDefaultValue("true"); - property.setHelpText(INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT); - configProperties.add(property); - property = new ProviderConfigProperty(); - property.setName(INCLUDE_IN_USERINFO); - property.setLabel(INCLUDE_IN_USERINFO_LABEL); - property.setType(ProviderConfigProperty.BOOLEAN_TYPE); - property.setDefaultValue("false"); - property.setHelpText(INCLUDE_IN_USERINFO_HELP_TEXT); - configProperties.add(property); + } + + public static void addIncludeInTokensConfig(List configProperties, Class protocolMapperClass) { + if (OIDCIDTokenMapper.class.isAssignableFrom(protocolMapperClass)) { + ProviderConfigProperty property = new ProviderConfigProperty(); + property.setName(INCLUDE_IN_ID_TOKEN); + property.setLabel(INCLUDE_IN_ID_TOKEN_LABEL); + property.setType(ProviderConfigProperty.BOOLEAN_TYPE); + property.setDefaultValue("true"); + property.setHelpText(INCLUDE_IN_ID_TOKEN_HELP_TEXT); + configProperties.add(property); + } + + if (OIDCAccessTokenMapper.class.isAssignableFrom(protocolMapperClass)) { + ProviderConfigProperty property = new ProviderConfigProperty(); + property.setName(INCLUDE_IN_ACCESS_TOKEN); + property.setLabel(INCLUDE_IN_ACCESS_TOKEN_LABEL); + property.setType(ProviderConfigProperty.BOOLEAN_TYPE); + property.setDefaultValue("true"); + property.setHelpText(INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT); + configProperties.add(property); + } + + if (UserInfoTokenMapper.class.isAssignableFrom(protocolMapperClass)) { + ProviderConfigProperty property = new ProviderConfigProperty(); + property.setName(INCLUDE_IN_USERINFO); + property.setLabel(INCLUDE_IN_USERINFO_LABEL); + property.setType(ProviderConfigProperty.BOOLEAN_TYPE); + property.setDefaultValue("true"); + property.setHelpText(INCLUDE_IN_USERINFO_HELP_TEXT); + configProperties.add(property); + } } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/PairwiseSubMapperHelper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/PairwiseSubMapperHelper.java index 0f6138aac4..d0d1398e2b 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/PairwiseSubMapperHelper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/PairwiseSubMapperHelper.java @@ -3,10 +3,8 @@ package org.keycloak.protocol.oidc.mappers; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.representations.idm.ProtocolMapperRepresentation; -import org.keycloak.services.ServicesLogger; public class PairwiseSubMapperHelper { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; public static final String SECTOR_IDENTIFIER_URI = "sectorIdentifierUri"; public static final String SECTOR_IDENTIFIER_URI_LABEL = "sectorIdentifierUri.label"; diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java index 5f54c070ca..fcdc373904 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java @@ -25,6 +25,7 @@ import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.representations.AccessToken; +import org.keycloak.representations.IDToken; import java.util.ArrayList; import java.util.HashMap; @@ -88,8 +89,8 @@ public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc } @Override - public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session2, - UserSessionModel userSession2, ClientSessionModel clientSessio2n) { + public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, + UserSessionModel userSession, ClientSessionModel clientSession) { String role = mappingModel.getConfig().get(ROLE_CONFIG); String newName = mappingModel.getConfig().get(NEW_ROLE_NAME); @@ -120,6 +121,7 @@ public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc } else { access = token.addAccess(newAppName); } + access.addRole(newRoleName); return token; } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA256PairwiseSubMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA256PairwiseSubMapper.java index fdb1e94aed..28a0b87284 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA256PairwiseSubMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA256PairwiseSubMapper.java @@ -1,5 +1,6 @@ package org.keycloak.protocol.oidc.mappers; +import org.jboss.logging.Logger; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperContainerModel; import org.keycloak.models.ProtocolMapperModel; @@ -9,7 +10,6 @@ import org.keycloak.protocol.ProtocolMapperConfigException; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.representations.idm.ProtocolMapperRepresentation; -import org.keycloak.services.ServicesLogger; import java.nio.charset.Charset; import java.security.MessageDigest; @@ -23,7 +23,7 @@ import java.util.UUID; public class SHA256PairwiseSubMapper extends AbstractPairwiseSubMapper { public static final String PROVIDER_ID = "sha256"; private static final String HASH_ALGORITHM = "SHA-256"; - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(SHA256PairwiseSubMapper.class); private final Charset charset; public SHA256PairwiseSubMapper() { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java index 56e7a48d2e..e6d0d209f5 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java @@ -51,7 +51,7 @@ public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements O property.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT); property.setType(ProviderConfigProperty.STRING_TYPE); configProperties.add(property); - OIDCAttributeMapperHelper.addAttributeConfig(configProperties); + OIDCAttributeMapperHelper.addAttributeConfig(configProperties, UserAttributeMapper.class); property = new ProviderConfigProperty(); property.setName(ProtocolMapperUtils.MULTIVALUED); @@ -89,16 +89,8 @@ public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements O return "Map a custom user attribute to a token claim."; } - @Override - public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, - UserSessionModel userSession, ClientSessionModel clientSession) { - if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) return token; - - setClaim(token, mappingModel, userSession); - return token; - } - protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) { + UserModel user = userSession.getUser(); String attributeName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE); List attributeValue = KeycloakModelUtils.resolveAttribute(user, attributeName); @@ -106,24 +98,6 @@ public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements O OIDCAttributeMapperHelper.mapClaim(token, mappingModel, attributeValue); } - @Override - public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { - if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) return token; - setClaim(token, mappingModel, userSession); - return token; - } - - @Override - public AccessToken transformUserInfoToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { - - if (!OIDCAttributeMapperHelper.includeInUserInfo(mappingModel)) { - return token; - } - - setClaim(token, mappingModel, userSession); - return token; - } - public static ProtocolMapperModel createClaimMapper(String name, String userAttribute, String tokenClaimName, String claimType, diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserClientRoleMappingMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserClientRoleMappingMapper.java index dcb55a8fa3..01d47e13d7 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserClientRoleMappingMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserClientRoleMappingMapper.java @@ -47,7 +47,7 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper { clientId.setName(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_CLIENT_ID); clientId.setLabel(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_CLIENT_ID_LABEL); clientId.setHelpText(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_CLIENT_ID_HELP_TEXT); - clientId.setType(ProviderConfigProperty.STRING_TYPE); + clientId.setType(ProviderConfigProperty.CLIENT_LIST_TYPE); CONFIG_PROPERTIES.add(clientId); ProviderConfigProperty clientRolePrefix = new ProviderConfigProperty(); @@ -57,7 +57,7 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper { clientRolePrefix.setType(ProviderConfigProperty.STRING_TYPE); CONFIG_PROPERTIES.add(clientRolePrefix); - OIDCAttributeMapperHelper.addAttributeConfig(CONFIG_PROPERTIES); + OIDCAttributeMapperHelper.addAttributeConfig(CONFIG_PROPERTIES, UserClientRoleMappingMapper.class); } public List getConfigProperties() { @@ -100,4 +100,21 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper { OIDCAttributeMapperHelper.mapClaim(token, mappingModel, clientRoleNames); } } + + + public static ProtocolMapperModel create(String clientId, String clientRolePrefix, + String name, + String tokenClaimName, + boolean accessToken, boolean idToken) { + ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo", + tokenClaimName, "String", + true, name, + accessToken, idToken, + PROVIDER_ID); + + mapper.getConfig().put(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_CLIENT_ID, clientId); + mapper.getConfig().put(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_ROLE_PREFIX, clientRolePrefix); + return mapper; + + } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserInfoTokenMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserInfoTokenMapper.java index a93e62b342..67ac1a2797 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserInfoTokenMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserInfoTokenMapper.java @@ -29,5 +29,5 @@ import org.keycloak.representations.AccessToken; public interface UserInfoTokenMapper { AccessToken transformUserInfoToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, - UserSessionModel userSession, ClientSessionModel clientSession); + UserSessionModel userSession, ClientSessionModel clientSession); } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java index e0ea4c311e..6fd649199d 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java @@ -38,7 +38,7 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { +public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { private static final List configProperties = new ArrayList(); static { @@ -49,7 +49,7 @@ public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OI property.setType(ProviderConfigProperty.STRING_TYPE); property.setHelpText(ProtocolMapperUtils.USER_MODEL_PROPERTY_HELP_TEXT); configProperties.add(property); - OIDCAttributeMapperHelper.addAttributeConfig(configProperties); + OIDCAttributeMapperHelper.addAttributeConfig(configProperties, UserPropertyMapper.class); } public static final String PROVIDER_ID = "oidc-usermodel-property-mapper"; @@ -79,24 +79,8 @@ public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OI return "Map a built in user property (email, firstName, lastName) to a token claim."; } - @Override - public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, - UserSessionModel userSession, ClientSessionModel clientSession) { - if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) return token; - setClaim(token, mappingModel, userSession); - - return token; - } - - @Override - public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { - if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) return token; - setClaim(token, mappingModel, userSession); - - return token; - } - protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) { + UserModel user = userSession.getUser(); String propertyName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE); String propertyValue = ProtocolMapperUtils.getUserModelValue(user, propertyName); @@ -114,6 +98,4 @@ public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OI accessToken, idToken, PROVIDER_ID); } - - } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserRealmRoleMappingMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserRealmRoleMappingMapper.java index 33460708a9..ef9818227b 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserRealmRoleMappingMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserRealmRoleMappingMapper.java @@ -21,11 +21,14 @@ import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.ProtocolMapperUtils; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.representations.IDToken; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -48,7 +51,7 @@ public class UserRealmRoleMappingMapper extends AbstractUserRoleMappingMapper { realmRolePrefix.setType(ProviderConfigProperty.STRING_TYPE); CONFIG_PROPERTIES.add(realmRolePrefix); - OIDCAttributeMapperHelper.addAttributeConfig(CONFIG_PROPERTIES); + OIDCAttributeMapperHelper.addAttributeConfig(CONFIG_PROPERTIES, UserRealmRoleMappingMapper.class); } public List getConfigProperties() { @@ -80,8 +83,23 @@ public class UserRealmRoleMappingMapper extends AbstractUserRoleMappingMapper { UserModel user = userSession.getUser(); String rolePrefix = mappingModel.getConfig().get(ProtocolMapperUtils.USER_MODEL_REALM_ROLE_MAPPING_ROLE_PREFIX); - Set realmRoleNames = flattenRoleModelToRoleNames(user.getRoleMappings(), rolePrefix); + Set realmRoleNames = flattenRoleModelToRoleNames(user.getRealmRoleMappings(), rolePrefix); OIDCAttributeMapperHelper.mapClaim(token, mappingModel, realmRoleNames); } + + + public static ProtocolMapperModel create(String realmRolePrefix, + String name, + String tokenClaimName, boolean accessToken, boolean idToken) { + ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo", + tokenClaimName, "String", + true, name, + accessToken, idToken, + PROVIDER_ID); + + mapper.getConfig().put(ProtocolMapperUtils.USER_MODEL_REALM_ROLE_MAPPING_ROLE_PREFIX, realmRolePrefix); + return mapper; + + } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java index 0016103b61..fd6bfe1c68 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java @@ -49,7 +49,7 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements property.setHelpText(ProtocolMapperUtils.USER_SESSION_MODEL_NOTE_HELP_TEXT); property.setType(ProviderConfigProperty.STRING_TYPE); configProperties.add(property); - OIDCAttributeMapperHelper.addAttributeConfig(configProperties); + OIDCAttributeMapperHelper.addAttributeConfig(configProperties, UserSessionNoteMapper.class); } public static final String PROVIDER_ID = "oidc-usersessionmodel-note-mapper"; @@ -79,29 +79,14 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements return "Map a custom user session note to a token claim."; } - @Override - public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, - UserSessionModel userSession, ClientSessionModel clientSession) { - if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) return token; - - setClaim(token, mappingModel, userSession); - return token; - } - protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) { + String noteName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_SESSION_NOTE); String noteValue = userSession.getNote(noteName); if (noteValue == null) return; OIDCAttributeMapperHelper.mapClaim(token, mappingModel, noteValue); } - @Override - public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { - if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) return token; - setClaim(token, mappingModel, userSession); - return token; - } - public static ProtocolMapperModel createClaimMapper(String name, String userSessionNote, String tokenClaimName, String jsonType, diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/PairwiseSubMapperUtils.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/PairwiseSubMapperUtils.java index 7535e6546b..75a86c885b 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/utils/PairwiseSubMapperUtils.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/PairwiseSubMapperUtils.java @@ -1,9 +1,9 @@ package org.keycloak.protocol.oidc.utils; +import org.jboss.logging.Logger; import org.keycloak.protocol.oidc.mappers.AbstractPairwiseSubMapper; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; -import org.keycloak.services.ServicesLogger; import java.net.URI; import java.net.URISyntaxException; @@ -16,7 +16,7 @@ import java.util.Set; import java.util.stream.Collectors; public class PairwiseSubMapperUtils { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(PairwiseSubMapperUtils.class); /** * Returns a set of valid redirect URIs from the root url and redirect URIs registered on a client. diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java index 125769e5d4..03bc9e6dfb 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java @@ -17,10 +17,10 @@ package org.keycloak.protocol.oidc.utils; +import org.jboss.logging.Logger; import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; import org.keycloak.models.RealmModel; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.Urls; import javax.ws.rs.core.UriInfo; @@ -33,7 +33,7 @@ import java.util.Set; */ public class RedirectUtils { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(RedirectUtils.class); public static String verifyRealmRedirectUri(UriInfo uriInfo, String redirectUri, RealmModel realm) { Set validRedirects = getValidateRedirectUris(uriInfo, realm); diff --git a/services/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java b/services/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java index 59ddd49edd..c1cf9c4b73 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java +++ b/services/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java @@ -85,7 +85,7 @@ public class AttributeStatementHelper { types.add(AttributeStatementHelper.URI_REFERENCE); types.add(AttributeStatementHelper.UNSPECIFIED); property.setType(ProviderConfigProperty.LIST_TYPE); - property.setDefaultValue(types); + property.setOptions(types); configProperties.add(property); } diff --git a/services/src/main/java/org/keycloak/protocol/saml/mappers/GroupMembershipMapper.java b/services/src/main/java/org/keycloak/protocol/saml/mappers/GroupMembershipMapper.java index 289c40e1a6..1a2db26f59 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/mappers/GroupMembershipMapper.java +++ b/services/src/main/java/org/keycloak/protocol/saml/mappers/GroupMembershipMapper.java @@ -65,7 +65,7 @@ public class GroupMembershipMapper extends AbstractSAMLProtocolMapper implements types.add(AttributeStatementHelper.URI_REFERENCE); types.add(AttributeStatementHelper.UNSPECIFIED); property.setType(ProviderConfigProperty.LIST_TYPE); - property.setDefaultValue(types); + property.setOptions(types); configProperties.add(property); property = new ProviderConfigProperty(); property.setName(SINGLE_GROUP_ATTRIBUTE); diff --git a/services/src/main/java/org/keycloak/protocol/saml/mappers/RoleListMapper.java b/services/src/main/java/org/keycloak/protocol/saml/mappers/RoleListMapper.java index 82d26d3c2b..dd274728e9 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/mappers/RoleListMapper.java +++ b/services/src/main/java/org/keycloak/protocol/saml/mappers/RoleListMapper.java @@ -69,7 +69,7 @@ public class RoleListMapper extends AbstractSAMLProtocolMapper implements SAMLRo types.add(AttributeStatementHelper.URI_REFERENCE); types.add(AttributeStatementHelper.UNSPECIFIED); property.setType(ProviderConfigProperty.LIST_TYPE); - property.setDefaultValue(types); + property.setOptions(types); configProperties.add(property); property = new ProviderConfigProperty(); property.setName(SINGLE_ROLE_ATTRIBUTE); diff --git a/services/src/main/java/org/keycloak/provider/FileSystemProviderLoaderFactory.java b/services/src/main/java/org/keycloak/provider/FileSystemProviderLoaderFactory.java index 2007d4b3e7..414bea80f3 100644 --- a/services/src/main/java/org/keycloak/provider/FileSystemProviderLoaderFactory.java +++ b/services/src/main/java/org/keycloak/provider/FileSystemProviderLoaderFactory.java @@ -16,7 +16,7 @@ */ package org.keycloak.provider; -import org.keycloak.services.ServicesLogger; +import org.jboss.logging.Logger; import java.io.File; import java.io.FilenameFilter; @@ -30,7 +30,7 @@ import java.util.List; */ public class FileSystemProviderLoaderFactory implements ProviderLoaderFactory { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(FileSystemProviderLoaderFactory.class); @Override public boolean supports(String type) { diff --git a/services/src/main/java/org/keycloak/provider/ProviderManager.java b/services/src/main/java/org/keycloak/provider/ProviderManager.java index e5d9712993..9db3181026 100644 --- a/services/src/main/java/org/keycloak/provider/ProviderManager.java +++ b/services/src/main/java/org/keycloak/provider/ProviderManager.java @@ -16,8 +16,8 @@ */ package org.keycloak.provider; +import org.jboss.logging.Logger; import org.keycloak.common.util.MultivaluedHashMap; -import org.keycloak.services.ServicesLogger; import java.util.Collections; import java.util.HashMap; @@ -32,7 +32,7 @@ import java.util.ServiceLoader; */ public class ProviderManager { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(ProviderManager.class); private List loaders = new LinkedList(); private MultivaluedHashMap, ProviderFactory> cache = new MultivaluedHashMap<>(); diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java index c26dceda0d..7fbd7a3d12 100644 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java @@ -227,6 +227,11 @@ public class DefaultKeycloakSession implements KeycloakSession { return providers; } + @Override + public Class getProviderClass(String providerClassName) { + return factory.getProviderClass(providerClassName); + } + @Override public RealmProvider realms() { if (model == null) { diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java index caf281a046..bd28e208bf 100755 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java @@ -16,6 +16,7 @@ */ package org.keycloak.services; +import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.models.KeycloakSession; @@ -40,7 +41,7 @@ import java.util.concurrent.CopyOnWriteArrayList; public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, ProviderManagerDeployer { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(DefaultKeycloakSessionFactory.class); private Set spis = new HashSet<>(); private Map, String> provider = new HashMap<>(); @@ -193,7 +194,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr factory.init(scope); if (spi.isInternal() && !isInternal(factory)) { - logger.spiMayChange(factory.getId(), factory.getClass().getName(), spi.getName()); + ServicesLogger.LOGGER.spiMayChange(factory.getId(), factory.getClass().getName(), spi.getName()); } factories.put(factory.getId(), factory); @@ -208,7 +209,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr factory.init(scope); if (spi.isInternal() && !isInternal(factory)) { - logger.spiMayChange(factory.getId(), factory.getClass().getName(), spi.getName()); + ServicesLogger.LOGGER.spiMayChange(factory.getId(), factory.getClass().getName(), spi.getName()); } factories.put(factory.getId(), factory); @@ -251,7 +252,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr factory.init(scope); if (spi.isInternal() && !isInternal(factory)) { - logger.spiMayChange(factory.getId(), factory.getClass().getName(), spi.getName()); + ServicesLogger.LOGGER.spiMayChange(factory.getId(), factory.getClass().getName(), spi.getName()); } factories.put(factory.getId(), factory); @@ -264,7 +265,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr factory.init(scope); if (spi.isInternal() && !isInternal(factory)) { - logger.spiMayChange(factory.getId(), factory.getClass().getName(), spi.getName()); + ServicesLogger.LOGGER.spiMayChange(factory.getId(), factory.getClass().getName(), spi.getName()); } factories.put(factory.getId(), factory); @@ -339,6 +340,15 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr return ids; } + Class getProviderClass(String providerClassName) { + for (Class clazz : factoriesMap.keySet()) { + if (clazz.getName().equals(providerClassName)) { + return clazz; + } + } + return null; + } + public void close() { ProviderManagerRegistry.SINGLETON.setDeployer(null); for (Map factories : factoriesMap.values()) { diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakTransactionManager.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakTransactionManager.java index 81379a1cdf..0039fa01e7 100755 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakTransactionManager.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakTransactionManager.java @@ -16,6 +16,7 @@ */ package org.keycloak.services; +import org.jboss.logging.Logger; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakTransaction; import org.keycloak.models.KeycloakTransactionManager; @@ -31,7 +32,7 @@ import java.util.List; */ public class DefaultKeycloakTransactionManager implements KeycloakTransactionManager { - public static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(DefaultKeycloakTransactionManager.class); private List prepare = new LinkedList(); private List transactions = new LinkedList(); @@ -140,7 +141,7 @@ public class DefaultKeycloakTransactionManager implements KeycloakTransactionMan try { tx.rollback(); } catch (RuntimeException e) { - logger.exceptionDuringRollback(e); + ServicesLogger.LOGGER.exceptionDuringRollback(e); } } } diff --git a/services/src/main/java/org/keycloak/services/ServicesLogger.java b/services/src/main/java/org/keycloak/services/ServicesLogger.java index 6418a61986..b0e0ccaf9c 100644 --- a/services/src/main/java/org/keycloak/services/ServicesLogger.java +++ b/services/src/main/java/org/keycloak/services/ServicesLogger.java @@ -47,7 +47,7 @@ import static org.jboss.logging.Logger.Level.WARN; @MessageLogger(projectCode="KC-SERVICES", length=4) public interface ServicesLogger extends BasicLogger { - ServicesLogger ROOT_LOGGER = Logger.getMessageLogger(ServicesLogger.class, "org.keycloak.services"); + ServicesLogger LOGGER = Logger.getMessageLogger(ServicesLogger.class, "org.keycloak.services"); @LogMessage(level = INFO) @Message(id=1, value="Loading config from %s") diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java index 034ba9dd4b..89f93f0166 100755 --- a/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java @@ -16,8 +16,8 @@ */ package org.keycloak.services.clientregistration.oidc; +import org.jboss.logging.Logger; import org.keycloak.common.util.Time; -import org.keycloak.events.EventBuilder; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; @@ -33,13 +33,8 @@ import org.keycloak.representations.oidc.OIDCClientRepresentation; import org.keycloak.services.ErrorResponseException; import org.keycloak.services.ServicesLogger; import org.keycloak.services.clientregistration.AbstractClientRegistrationProvider; -import org.keycloak.services.clientregistration.ClientRegistrationAuth; -import org.keycloak.services.clientregistration.ClientRegistrationContext; -import org.keycloak.services.clientregistration.DefaultClientRegistrationContext; import org.keycloak.services.clientregistration.ClientRegistrationException; import org.keycloak.services.clientregistration.ErrorCodes; -import org.keycloak.services.validation.PairwiseClientValidator; -import org.keycloak.services.validation.ValidationMessages; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -52,10 +47,8 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; -import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -63,7 +56,7 @@ import java.util.concurrent.atomic.AtomicBoolean; */ public class OIDCClientRegistrationProvider extends AbstractClientRegistrationProvider { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(OIDCClientRegistrationProvider.class); public OIDCClientRegistrationProvider(KeycloakSession session) { super(session); @@ -91,7 +84,7 @@ public class OIDCClientRegistrationProvider extends AbstractClientRegistrationPr clientOIDC.setClientIdIssuedAt(Time.currentTime()); return Response.created(uri).entity(clientOIDC).build(); } catch (ClientRegistrationException cre) { - logger.clientRegistrationException(cre.getMessage()); + ServicesLogger.LOGGER.clientRegistrationException(cre.getMessage()); throw new ErrorResponseException(ErrorCodes.INVALID_CLIENT_METADATA, "Client metadata invalid", Response.Status.BAD_REQUEST); } } @@ -122,7 +115,7 @@ public class OIDCClientRegistrationProvider extends AbstractClientRegistrationPr clientOIDC = DescriptionConverter.toExternalResponse(session, client, uri); return Response.ok(clientOIDC).build(); } catch (ClientRegistrationException cre) { - logger.clientRegistrationException(cre.getMessage()); + ServicesLogger.LOGGER.clientRegistrationException(cre.getMessage()); throw new ErrorResponseException(ErrorCodes.INVALID_CLIENT_METADATA, "Client metadata invalid", Response.Status.BAD_REQUEST); } } diff --git a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientTemplatesClientRegistrationPolicyFactory.java b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientTemplatesClientRegistrationPolicyFactory.java index c886e8f34c..cc71424d7a 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientTemplatesClientRegistrationPolicyFactory.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientTemplatesClientRegistrationPolicyFactory.java @@ -60,7 +60,7 @@ public class ClientTemplatesClientRegistrationPolicyFactory extends AbstractClie property.setType(ProviderConfigProperty.MULTIVALUED_LIST_TYPE); if (session != null) { - property.setDefaultValue(getClientTemplates(session)); + property.setOptions(getClientTemplates(session)); } configProperties = Collections.singletonList(property); diff --git a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicyFactory.java b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicyFactory.java index e794e3cfdb..a9aea1420a 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicyFactory.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicyFactory.java @@ -58,7 +58,7 @@ public class ProtocolMappersClientRegistrationPolicyFactory extends AbstractClie property.setLabel("allowed-protocol-mappers.label"); property.setHelpText("allowed-protocol-mappers.tooltip"); property.setType(ProviderConfigProperty.MULTIVALUED_LIST_TYPE); - property.setDefaultValue(getProtocolMapperFactoryIds()); + property.setOptions(getProtocolMapperFactoryIds()); configProperties.add(property); property = new ProviderConfigProperty(); diff --git a/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java b/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java index 89cf129958..7de8ee4622 100755 --- a/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java +++ b/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java @@ -27,7 +27,6 @@ import org.keycloak.models.RoleModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; import org.keycloak.models.utils.DefaultKeyProviders; -import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.services.ServicesLogger; @@ -37,7 +36,6 @@ import org.keycloak.services.ServicesLogger; */ public class ApplianceBootstrap { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; private final KeycloakSession session; public ApplianceBootstrap(KeycloakSession session) { @@ -63,7 +61,7 @@ public class ApplianceBootstrap { } String adminRealmName = Config.getAdminRealm(); - logger.initializingAdminRealm(adminRealmName); + ServicesLogger.LOGGER.initializingAdminRealm(adminRealmName); RealmManager manager = new RealmManager(session); manager.setContextPath(contextPath); diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index 4b334070b6..021011aca3 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -224,7 +224,7 @@ public class AuthenticationManager { protocol.backchannelLogout(userSession, clientSession); clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT.name()); } catch (Exception e) { - ServicesLogger.ROOT_LOGGER.failedToLogoutClient(e); + ServicesLogger.LOGGER.failedToLogoutClient(e); } } } @@ -245,7 +245,7 @@ public class AuthenticationManager { return response; } } catch (Exception e) { - ServicesLogger.ROOT_LOGGER.failedToLogoutClient(e); + ServicesLogger.LOGGER.failedToLogoutClient(e); } } diff --git a/services/src/main/java/org/keycloak/services/managers/ClientManager.java b/services/src/main/java/org/keycloak/services/managers/ClientManager.java index 707dc34ee9..b648bbd6b8 100644 --- a/services/src/main/java/org/keycloak/services/managers/ClientManager.java +++ b/services/src/main/java/org/keycloak/services/managers/ClientManager.java @@ -18,6 +18,7 @@ package org.keycloak.services.managers; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import org.jboss.logging.Logger; import org.keycloak.authentication.ClientAuthenticator; import org.keycloak.authentication.ClientAuthenticatorFactory; import org.keycloak.common.constants.ServiceAccountConstants; @@ -37,7 +38,6 @@ import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper; import org.keycloak.representations.adapters.config.BaseRealmConfig; import org.keycloak.representations.adapters.config.PolicyEnforcerConfig; import org.keycloak.representations.idm.ClientRepresentation; -import org.keycloak.services.ServicesLogger; import java.net.URI; import java.util.Collections; @@ -52,7 +52,7 @@ import java.util.TreeSet; * @version $Revision: 1 $ */ public class ClientManager { - protected ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(ClientManager.class); protected RealmManager realmManager; diff --git a/services/src/main/java/org/keycloak/services/managers/DefaultBruteForceProtector.java b/services/src/main/java/org/keycloak/services/managers/DefaultBruteForceProtector.java index 33c81c11f2..76c9cc76af 100644 --- a/services/src/main/java/org/keycloak/services/managers/DefaultBruteForceProtector.java +++ b/services/src/main/java/org/keycloak/services/managers/DefaultBruteForceProtector.java @@ -17,6 +17,7 @@ package org.keycloak.services.managers; +import org.jboss.logging.Logger; import org.keycloak.common.ClientConnection; import org.keycloak.common.util.Time; import org.keycloak.models.KeycloakSession; @@ -39,7 +40,7 @@ import java.util.concurrent.TimeUnit; * @version $Revision: 1 $ */ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(DefaultBruteForceProtector.class); protected volatile boolean run = true; protected int maxDeltaTimeSeconds = 60 * 60 * 12; // 12 hours @@ -202,7 +203,7 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector session.close(); } } catch (Exception e) { - logger.failedProcessingType(e); + ServicesLogger.LOGGER.failedProcessingType(e); } } catch (InterruptedException e) { break; @@ -214,7 +215,7 @@ public class DefaultBruteForceProtector implements Runnable, BruteForceProtector } protected void logFailure(LoginEvent event) { - logger.loginFailure(event.userId, event.ip); + ServicesLogger.LOGGER.loginFailure(event.userId, event.ip); failures++; long delta = 0; if (lastFailure > 0) { diff --git a/services/src/main/java/org/keycloak/services/managers/LDAPConnectionTestManager.java b/services/src/main/java/org/keycloak/services/managers/LDAPConnectionTestManager.java index b62d1d9c97..94eb6b7f2b 100755 --- a/services/src/main/java/org/keycloak/services/managers/LDAPConnectionTestManager.java +++ b/services/src/main/java/org/keycloak/services/managers/LDAPConnectionTestManager.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.managers; +import org.jboss.logging.Logger; import org.keycloak.models.LDAPConstants; import org.keycloak.services.ServicesLogger; @@ -29,14 +30,14 @@ import java.util.Hashtable; */ public class LDAPConnectionTestManager { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(LDAPConnectionTestManager.class); public static final String TEST_CONNECTION = "testConnection"; public static final String TEST_AUTHENTICATION = "testAuthentication"; public boolean testLDAP(String action, String connectionUrl, String bindDn, String bindCredential, String useTruststoreSpi) { if (!TEST_CONNECTION.equals(action) && !TEST_AUTHENTICATION.equals(action)) { - logger.unknownAction(action); + ServicesLogger.LOGGER.unknownAction(action); return false; } @@ -73,14 +74,14 @@ public class LDAPConnectionTestManager { return true; } catch (Exception ne) { String errorMessage = (TEST_AUTHENTICATION.equals(action)) ? "Error when authenticating to LDAP: " : "Error when connecting to LDAP: "; - logger.errorAuthenticating(ne, errorMessage + ne.getMessage()); + ServicesLogger.LOGGER.errorAuthenticating(ne, errorMessage + ne.getMessage()); return false; } finally { if (ldapContext != null) { try { ldapContext.close(); } catch (NamingException ne) { - logger.errorClosingLDAP(ne); + ServicesLogger.LOGGER.errorClosingLDAP(ne); } } } diff --git a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java index 488df50b4e..12e0449b32 100755 --- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java +++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.managers; +import org.jboss.logging.Logger; import org.keycloak.TokenIdGenerator; import org.keycloak.common.util.KeycloakUriBuilder; import org.keycloak.common.util.MultivaluedHashMap; @@ -53,7 +54,7 @@ import java.util.Set; * @version $Revision: 1 $ */ public class ResourceAdminManager { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(ResourceAdminManager.class); private static final String CLIENT_SESSION_HOST_PROPERTY = "${application.session.host}"; private KeycloakSession session; @@ -253,7 +254,7 @@ public class ResourceAdminManager { logger.debugf("logout success for %s: %s", managementUrl, success); return success; } catch (IOException e) { - logger.logoutFailed(e, resource.getClientId()); + ServicesLogger.LOGGER.logoutFailed(e, resource.getClientId()); return false; } } @@ -304,7 +305,7 @@ public class ResourceAdminManager { logger.debugf("pushRevocation success for %s: %s", managementUrl, success); return success; } catch (IOException e) { - logger.failedToSendRevocation(e); + ServicesLogger.LOGGER.failedToSendRevocation(e); return false; } } @@ -342,7 +343,7 @@ public class ResourceAdminManager { logger.debugf("testAvailability success for %s: %s", managementUrl, success); return success; } catch (IOException e) { - logger.availabilityTestFailed(managementUrl); + ServicesLogger.LOGGER.availabilityTestFailed(managementUrl); return false; } } diff --git a/services/src/main/java/org/keycloak/services/managers/UserSessionManager.java b/services/src/main/java/org/keycloak/services/managers/UserSessionManager.java index 639668f93b..e3ee409e49 100644 --- a/services/src/main/java/org/keycloak/services/managers/UserSessionManager.java +++ b/services/src/main/java/org/keycloak/services/managers/UserSessionManager.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.managers; +import org.jboss.logging.Logger; import org.keycloak.common.util.Time; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientSessionModel; @@ -41,7 +42,7 @@ import java.util.Set; */ public class UserSessionManager { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(UserSessionManager.class); private final KeycloakSession kcSession; private final UserSessionPersisterProvider persister; @@ -136,7 +137,7 @@ public class UserSessionManager { public boolean isOfflineTokenAllowed(ClientSessionModel clientSession) { RoleModel offlineAccessRole = clientSession.getRealm().getRole(Constants.OFFLINE_ACCESS_ROLE); if (offlineAccessRole == null) { - logger.roleNotInRealm(Constants.OFFLINE_ACCESS_ROLE); + ServicesLogger.LOGGER.roleNotInRealm(Constants.OFFLINE_ACCESS_ROLE); return false; } diff --git a/services/src/main/java/org/keycloak/services/managers/UserStorageSyncManager.java b/services/src/main/java/org/keycloak/services/managers/UserStorageSyncManager.java index eaca98fea1..05b07f2ed4 100755 --- a/services/src/main/java/org/keycloak/services/managers/UserStorageSyncManager.java +++ b/services/src/main/java/org/keycloak/services/managers/UserStorageSyncManager.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.managers; +import org.jboss.logging.Logger; import org.keycloak.cluster.ClusterEvent; import org.keycloak.cluster.ClusterListener; import org.keycloak.cluster.ClusterProvider; @@ -44,7 +45,7 @@ public class UserStorageSyncManager { private static final String USER_STORAGE_TASK_KEY = "user-storage"; - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(UserStorageSyncManager.class); /** * Check federationProviderModel of all realms and possibly start periodic sync for them @@ -194,7 +195,7 @@ public class UserStorageSyncManager { logger.debugf("Ignored periodic full sync with storage provider %s due small time since last sync", provider.getName()); } } catch (Throwable t) { - logger.errorDuringFullUserSync(t); + ServicesLogger.LOGGER.errorDuringFullUserSync(t); } } @@ -217,7 +218,7 @@ public class UserStorageSyncManager { logger.debugf("Ignored periodic changed-users sync with storage provider %s due small time since last sync", provider.getName()); } } catch (Throwable t) { - logger.errorDuringChangedUserSync(t); + ServicesLogger.LOGGER.errorDuringChangedUserSync(t); } } diff --git a/services/src/main/java/org/keycloak/services/managers/UsersSyncManager.java b/services/src/main/java/org/keycloak/services/managers/UsersSyncManager.java index 7d390ba2a6..d8c5dee5ad 100755 --- a/services/src/main/java/org/keycloak/services/managers/UsersSyncManager.java +++ b/services/src/main/java/org/keycloak/services/managers/UsersSyncManager.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.managers; +import org.jboss.logging.Logger; import org.keycloak.cluster.ClusterEvent; import org.keycloak.cluster.ClusterListener; import org.keycloak.cluster.ClusterProvider; @@ -43,7 +44,7 @@ public class UsersSyncManager { private static final String FEDERATION_TASK_KEY = "federation"; - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(UsersSyncManager.class); /** * Check federationProviderModel of all realms and possibly start periodic sync for them @@ -177,7 +178,7 @@ public class UsersSyncManager { logger.debugf("Ignored periodic full sync with federation provider %s due small time since last sync", fedProvider.getDisplayName()); } } catch (Throwable t) { - logger.errorDuringFullUserSync(t); + ServicesLogger.LOGGER.errorDuringFullUserSync(t); } } @@ -200,7 +201,7 @@ public class UsersSyncManager { logger.debugf("Ignored periodic changed-users sync with federation provider %s due small time since last sync", fedProvider.getDisplayName()); } } catch (Throwable t) { - logger.errorDuringChangedUserSync(t); + ServicesLogger.LOGGER.errorDuringChangedUserSync(t); } } diff --git a/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java b/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java index 17b4995c60..f5a1b270c5 100755 --- a/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java +++ b/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources; +import org.jboss.logging.Logger; import org.jboss.resteasy.spi.BadRequestException; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.AbstractOAuthClient; @@ -29,7 +30,6 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.oidc.OIDCLoginProtocolService; import org.keycloak.services.ForbiddenException; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.managers.AppAuthManager; import org.keycloak.services.managers.Auth; import org.keycloak.services.managers.AuthenticationManager; @@ -57,7 +57,7 @@ import java.util.Set; * @version $Revision: 1 $ */ public abstract class AbstractSecuredLocalService { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(AbstractSecuredLocalService.class); private static final String KEYCLOAK_STATE_CHECKER = "KEYCLOAK_STATE_CHECKER"; diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java index feee73e538..2684483497 100755 --- a/services/src/main/java/org/keycloak/services/resources/AccountService.java +++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources; +import org.jboss.logging.Logger; import org.keycloak.common.util.UriUtils; import org.keycloak.credential.CredentialModel; import org.keycloak.events.Details; @@ -86,7 +87,7 @@ import java.util.UUID; */ public class AccountService extends AbstractSecuredLocalService { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(AccountService.class); private static Set VALID_PATHS = new HashSet(); static { @@ -643,12 +644,12 @@ public class AccountService extends AbstractSecuredLocalService { errorEvent.error(Errors.NOT_ALLOWED); return account.setError(Messages.READ_ONLY_PASSWORD).createResponse(AccountPages.PASSWORD); } catch (ModelException me) { - logger.failedToUpdatePassword(me); + ServicesLogger.LOGGER.failedToUpdatePassword(me); setReferrerOnPage(); errorEvent.detail(Details.REASON, me.getMessage()).error(Errors.PASSWORD_REJECTED); return account.setError(me.getMessage(), me.getParameters()).createResponse(AccountPages.PASSWORD); } catch (Exception ape) { - logger.failedToUpdatePassword(ape); + ServicesLogger.LOGGER.failedToUpdatePassword(ape); setReferrerOnPage(); errorEvent.detail(Details.REASON, ape.getMessage()).error(Errors.PASSWORD_REJECTED); return account.setError(ape.getMessage()).createResponse(AccountPages.PASSWORD); diff --git a/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java b/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java index 5497b57a94..5170bc2ced 100755 --- a/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java +++ b/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources; +import org.jboss.logging.Logger; import org.jboss.resteasy.spi.BadRequestException; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.UnauthorizedException; @@ -33,7 +34,6 @@ import org.keycloak.models.RealmModel; import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil; import org.keycloak.representations.idm.OAuth2ErrorRepresentation; import org.keycloak.services.ForbiddenException; -import org.keycloak.services.ServicesLogger; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; @@ -53,7 +53,7 @@ import javax.ws.rs.ext.Providers; */ public class ClientsManagementService { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(ClientsManagementService.class); private RealmModel realm; diff --git a/services/src/main/java/org/keycloak/services/resources/Cors.java b/services/src/main/java/org/keycloak/services/resources/Cors.java index 7b4957adb4..2cd07b108a 100755 --- a/services/src/main/java/org/keycloak/services/resources/Cors.java +++ b/services/src/main/java/org/keycloak/services/resources/Cors.java @@ -16,12 +16,12 @@ */ package org.keycloak.services.resources; +import org.jboss.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpResponse; import org.keycloak.common.util.CollectionUtil; import org.keycloak.models.ClientModel; import org.keycloak.representations.AccessToken; -import org.keycloak.services.ServicesLogger; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; @@ -34,7 +34,7 @@ import java.util.concurrent.TimeUnit; * @author Stian Thorgersen */ public class Cors { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(Cors.class); public static final long DEFAULT_MAX_AGE = TimeUnit.HOURS.toSeconds(1); public static final String DEFAULT_ALLOW_METHODS = "GET, HEAD, OPTIONS"; diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java index 546438424e..a8c4cc4040 100755 --- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java +++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources; +import org.jboss.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.OAuth2Constants; @@ -99,7 +100,7 @@ import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID; */ public class IdentityBrokerService implements IdentityProvider.AuthenticationCallback { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(IdentityBrokerService.class); private final RealmModel realmModel; @@ -856,7 +857,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal this.session.getTransactionManager().commit(); } } catch (Exception e) { - logger.couldNotFireEvent(e); + ServicesLogger.LOGGER.couldNotFireEvent(e); rollback(); } } diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java index d425eeae76..363d6f4774 100644 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.jboss.dmr.ModelNode; +import org.jboss.logging.Logger; import org.jboss.resteasy.core.Dispatcher; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.Config; @@ -87,7 +88,7 @@ public class KeycloakApplication extends Application { public static final String KEYCLOAK_EMBEDDED = "keycloak.embedded"; - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(KeycloakApplication.class); protected boolean embedded = false; @@ -265,14 +266,14 @@ public class KeycloakApplication extends Application { String dmrConfig = loadDmrConfig(context); if (dmrConfig != null) { node = new ObjectMapper().readTree(dmrConfig); - logger.loadingFrom("standalone.xml or domain.xml"); + ServicesLogger.LOGGER.loadingFrom("standalone.xml or domain.xml"); } String configDir = System.getProperty("jboss.server.config.dir"); if (node == null && configDir != null) { File f = new File(configDir + File.separator + "keycloak-server.json"); if (f.isFile()) { - logger.loadingFrom(f.getAbsolutePath()); + ServicesLogger.LOGGER.loadingFrom(f.getAbsolutePath()); node = new ObjectMapper().readTree(f); } } @@ -280,7 +281,7 @@ public class KeycloakApplication extends Application { if (node == null) { URL resource = Thread.currentThread().getContextClassLoader().getResource("META-INF/keycloak-server.json"); if (resource != null) { - logger.loadingFrom(resource); + ServicesLogger.LOGGER.loadingFrom(resource); node = new ObjectMapper().readTree(resource); } } @@ -370,23 +371,23 @@ public class KeycloakApplication extends Application { manager.setContextPath(getContextPath()); if (rep.getId() != null && manager.getRealm(rep.getId()) != null) { - logger.realmExists(rep.getRealm(), from); + ServicesLogger.LOGGER.realmExists(rep.getRealm(), from); exists = true; } if (manager.getRealmByName(rep.getRealm()) != null) { - logger.realmExists(rep.getRealm(), from); + ServicesLogger.LOGGER.realmExists(rep.getRealm(), from); exists = true; } if (!exists) { RealmModel realm = manager.importRealm(rep); - logger.importedRealm(realm.getName(), from); + ServicesLogger.LOGGER.importedRealm(realm.getName(), from); } session.getTransactionManager().commit(); } catch (Throwable t) { session.getTransactionManager().rollback(); if (!exists) { - logger.unableToImportRealm(t, rep.getRealm(), from); + ServicesLogger.LOGGER.unableToImportRealm(t, rep.getRealm(), from); } } } finally { @@ -399,14 +400,14 @@ public class KeycloakApplication extends Application { if (configDir != null) { File addUserFile = new File(configDir + File.separator + "keycloak-add-user.json"); if (addUserFile.isFile()) { - logger.imprtingUsersFrom(addUserFile); + ServicesLogger.LOGGER.imprtingUsersFrom(addUserFile); List realms; try { realms = JsonSerialization.readValue(new FileInputStream(addUserFile), new TypeReference>() { }); } catch (IOException e) { - logger.failedToLoadUsers(e); + ServicesLogger.LOGGER.failedToLoadUsers(e); return; } @@ -418,7 +419,7 @@ public class KeycloakApplication extends Application { RealmModel realm = session.realms().getRealmByName(realmRep.getRealm()); if (realm == null) { - logger.addUserFailedRealmNotFound(userRep.getUsername(), realmRep.getRealm()); + ServicesLogger.LOGGER.addUserFailedRealmNotFound(userRep.getUsername(), realmRep.getRealm()); } else { UserModel user = session.users().addUser(realm, userRep.getUsername()); user.setEnabled(userRep.isEnabled()); @@ -427,13 +428,13 @@ public class KeycloakApplication extends Application { } session.getTransactionManager().commit(); - logger.addUserSuccess(userRep.getUsername(), realmRep.getRealm()); + ServicesLogger.LOGGER.addUserSuccess(userRep.getUsername(), realmRep.getRealm()); } catch (ModelDuplicateException e) { session.getTransactionManager().rollback(); - logger.addUserFailedUserExists(userRep.getUsername(), realmRep.getRealm()); + ServicesLogger.LOGGER.addUserFailedUserExists(userRep.getUsername(), realmRep.getRealm()); } catch (Throwable t) { session.getTransactionManager().rollback(); - logger.addUserFailed(t, userRep.getUsername(), realmRep.getRealm()); + ServicesLogger.LOGGER.addUserFailed(t, userRep.getUsername(), realmRep.getRealm()); } finally { session.close(); } @@ -441,7 +442,7 @@ public class KeycloakApplication extends Application { } if (!addUserFile.delete()) { - logger.failedToDeleteFile(addUserFile.getAbsolutePath()); + ServicesLogger.LOGGER.failedToDeleteFile(addUserFile.getAbsolutePath()); } } } diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java index f85c2a1d68..2e2641c009 100755 --- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java +++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources; +import org.jboss.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.OAuth2Constants; import org.keycloak.authentication.AuthenticationProcessor; @@ -86,7 +87,7 @@ import java.net.URI; */ public class LoginActionsService { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(LoginActionsService.class); public static final String ACTION_COOKIE = "KEYCLOAK_ACTION"; public static final String AUTHENTICATE_PATH = "authenticate"; @@ -239,7 +240,7 @@ public class LoginActionsService { return false; } } catch (Exception e) { - logger.failedToParseRestartLoginCookie(e); + ServicesLogger.LOGGER.failedToParseRestartLoginCookie(e); } } event.error(Errors.INVALID_CODE); @@ -281,7 +282,7 @@ public class LoginActionsService { final UserSessionModel userSession = clientSession.getUserSession(); if (userSession == null) { - logger.userSessionNull(); + ServicesLogger.LOGGER.userSessionNull(); event.error(Errors.USER_SESSION_NOT_FOUND); throw new WebApplicationException(ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE)); } @@ -579,7 +580,7 @@ public class LoginActionsService { String noteKey = firstBrokerLogin ? AbstractIdpAuthenticator.BROKERED_CONTEXT_NOTE : PostBrokerLoginConstants.PBL_BROKERED_IDENTITY_CONTEXT; SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromClientSession(clientSessionn, noteKey); if (serializedCtx == null) { - logger.notFoundSerializedCtxInClientSession(noteKey); + ServicesLogger.LOGGER.notFoundSerializedCtxInClientSession(noteKey); throw new WebApplicationException(ErrorPage.error(session, "Not found serialized context in clientSession.")); } BrokeredIdentityContext brokerContext = serializedCtx.deserialize(session, clientSessionn); @@ -587,12 +588,12 @@ public class LoginActionsService { String flowId = firstBrokerLogin ? brokerContext.getIdpConfig().getFirstBrokerLoginFlowId() : brokerContext.getIdpConfig().getPostBrokerLoginFlowId(); if (flowId == null) { - logger.flowNotConfigForIDP(identityProviderAlias); + ServicesLogger.LOGGER.flowNotConfigForIDP(identityProviderAlias); throw new WebApplicationException(ErrorPage.error(session, "Flow not configured for identity provider")); } AuthenticationFlowModel brokerLoginFlow = realm.getAuthenticationFlowById(flowId); if (brokerLoginFlow == null) { - logger.flowNotFoundForIDP(flowId, identityProviderAlias); + ServicesLogger.LOGGER.flowNotFoundForIDP(flowId, identityProviderAlias); throw new WebApplicationException(ErrorPage.error(session, "Flow not found for identity provider")); } @@ -702,7 +703,7 @@ public class LoginActionsService { ClientSessionCode accessCode = checks.clientCode; ClientSessionModel clientSession = accessCode.getClientSession(); if (!ClientSessionModel.Action.VERIFY_EMAIL.name().equals(clientSession.getNote(AuthenticationManager.CURRENT_REQUIRED_ACTION))) { - logger.reqdActionDoesNotMatch(); + ServicesLogger.LOGGER.reqdActionDoesNotMatch(); event.error(Errors.INVALID_CODE); throw new WebApplicationException(ErrorPage.error(session, Messages.STALE_VERIFY_EMAIL_LINK)); } @@ -715,7 +716,7 @@ public class LoginActionsService { String keyFromSession = clientSession.getNote(Constants.VERIFY_EMAIL_KEY); clientSession.removeNote(Constants.VERIFY_EMAIL_KEY); if (!key.equals(keyFromSession)) { - logger.invalidKeyForEmailVerification(); + ServicesLogger.LOGGER.invalidKeyForEmailVerification(); event.error(Errors.INVALID_USER_CREDENTIALS); throw new WebApplicationException(ErrorPage.error(session, Messages.INVALID_CODE)); } @@ -859,7 +860,7 @@ public class LoginActionsService { RequiredActionFactory factory = (RequiredActionFactory)session.getKeycloakSessionFactory().getProviderFactory(RequiredActionProvider.class, action); if (factory == null) { - logger.actionProviderNull(); + ServicesLogger.LOGGER.actionProviderNull(); event.error(Errors.INVALID_CODE); throw new WebApplicationException(ErrorPage.error(session, Messages.INVALID_CODE)); } diff --git a/services/src/main/java/org/keycloak/services/resources/PublicRealmResource.java b/services/src/main/java/org/keycloak/services/resources/PublicRealmResource.java index 0b41e519b1..227719da72 100755 --- a/services/src/main/java/org/keycloak/services/resources/PublicRealmResource.java +++ b/services/src/main/java/org/keycloak/services/resources/PublicRealmResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpResponse; @@ -24,7 +25,6 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.protocol.oidc.OIDCLoginProtocolService; import org.keycloak.representations.idm.PublishedRealmRepresentation; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.resources.admin.AdminRoot; import javax.ws.rs.GET; @@ -43,7 +43,7 @@ import javax.ws.rs.core.UriInfo; * @version $Revision: 1 $ */ public class PublicRealmResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(PublicRealmResource.class); @Context protected UriInfo uriInfo; diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java index a6cf0723b8..f30665be8b 100755 --- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources; +import org.jboss.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.authorization.AuthorizationProvider; @@ -29,7 +30,6 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.protocol.LoginProtocol; import org.keycloak.protocol.LoginProtocolFactory; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.clientregistration.ClientRegistrationService; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resource.RealmResourceProvider; @@ -57,7 +57,7 @@ import java.net.URI; */ @Path("/realms") public class RealmsResource { - protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(RealmsResource.class); @Context protected KeycloakSession session; diff --git a/services/src/main/java/org/keycloak/services/resources/ServerVersionResource.java b/services/src/main/java/org/keycloak/services/resources/ServerVersionResource.java index a3db764fca..0352c08169 100755 --- a/services/src/main/java/org/keycloak/services/resources/ServerVersionResource.java +++ b/services/src/main/java/org/keycloak/services/resources/ServerVersionResource.java @@ -16,11 +16,11 @@ */ package org.keycloak.services.resources; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpResponse; import org.keycloak.representations.VersionRepresentation; -import org.keycloak.services.ServicesLogger; import javax.ws.rs.GET; import javax.ws.rs.OPTIONS; @@ -37,7 +37,7 @@ import javax.ws.rs.core.Response; @Path("/version") public class ServerVersionResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(ServerVersionResource.class); @Context protected HttpRequest request; diff --git a/services/src/main/java/org/keycloak/services/resources/ThemeResource.java b/services/src/main/java/org/keycloak/services/resources/ThemeResource.java index 7cb2379c9a..5c29bfac58 100644 --- a/services/src/main/java/org/keycloak/services/resources/ThemeResource.java +++ b/services/src/main/java/org/keycloak/services/resources/ThemeResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources; +import org.jboss.logging.Logger; import org.keycloak.common.Version; import org.keycloak.common.util.MimeTypeUtil; import org.keycloak.models.KeycloakSession; @@ -39,7 +40,7 @@ import java.io.InputStream; @Path("/resources") public class ThemeResource { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(ThemeResource.class); @Context private KeycloakSession session; @@ -69,7 +70,7 @@ public class ThemeResource { return Response.status(Response.Status.NOT_FOUND).build(); } } catch (Exception e) { - logger.failedToGetThemeRequest(e); + ServicesLogger.LOGGER.failedToGetThemeRequest(e); return Response.serverError().build(); } } diff --git a/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java b/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java index d57f0fe224..48d2650618 100755 --- a/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java +++ b/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources; +import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.common.ClientConnection; import org.keycloak.common.util.MimeTypeUtil; @@ -63,7 +64,7 @@ import java.util.Map; @Path("/") public class WelcomeResource { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(WelcomeResource.class); private static final String KEYCLOAK_STATE_CHECKER = "KEYCLOAK_STATE_CHECKER"; @@ -110,7 +111,7 @@ public class WelcomeResource { return createWelcomePage(null, null); } else { if (!isLocal()) { - logger.rejectedNonLocalAttemptToCreateInitialUser(session.getContext().getConnection().getRemoteAddr()); + ServicesLogger.LOGGER.rejectedNonLocalAttemptToCreateInitialUser(session.getContext().getConnection().getRemoteAddr()); throw new WebApplicationException(Response.Status.BAD_REQUEST); } @@ -139,10 +140,10 @@ public class WelcomeResource { bootstrap = false; applianceBootstrap.createMasterRealmUser(username, password); - logger.createdInitialAdminUser(username); + ServicesLogger.LOGGER.createdInitialAdminUser(username); return createWelcomePage("User created", null); } else { - logger.initialUserAlreadyCreated(); + ServicesLogger.LOGGER.initialUserAlreadyCreated(); return createWelcomePage(null, "Users already exists"); } } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java index afbec5fb0a..cd4d881cf4 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java @@ -17,6 +17,7 @@ package org.keycloak.services.resources.admin; import com.fasterxml.jackson.annotation.JsonProperty; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpResponse; @@ -32,7 +33,6 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; import org.keycloak.protocol.oidc.OIDCLoginProtocolService; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.Urls; import org.keycloak.services.managers.AppAuthManager; import org.keycloak.services.managers.AuthenticationManager; @@ -70,7 +70,7 @@ import java.util.Set; * @version $Revision: 1 $ */ public class AdminConsole { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(AdminConsole.class); @Context protected UriInfo uriInfo; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java index 0fa484dc3e..39be678a80 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.keycloak.common.ClientConnection; import org.keycloak.common.util.Time; import org.keycloak.events.EventListenerProvider; @@ -38,7 +39,7 @@ import java.util.List; public class AdminEventBuilder { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(AdminEventBuilder.class); private EventStoreProvider store; private List listeners; @@ -54,7 +55,7 @@ public class AdminEventBuilder { if (store != null) { this.store = store; } else { - logger.noEventStoreProvider(); + ServicesLogger.LOGGER.noEventStoreProvider(); } } @@ -65,7 +66,7 @@ public class AdminEventBuilder { if (listener != null) { listeners.add(listener); } else { - logger.providerNotFound(id); + ServicesLogger.LOGGER.providerNotFound(id); } } } @@ -214,7 +215,7 @@ public class AdminEventBuilder { try { store.onEvent(adminEvent, includeRepresentation); } catch (Throwable t) { - logger.failedToSaveEvent(t); + ServicesLogger.LOGGER.failedToSaveEvent(t); } } @@ -223,7 +224,7 @@ public class AdminEventBuilder { try { l.onEvent(adminEvent, includeRepresentation); } catch (Throwable t) { - logger.failedToSendType(t, l); + ServicesLogger.LOGGER.failedToSendType(t, l); } } } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java index 87d9091428..5a6f4f1edd 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpResponse; import org.jboss.resteasy.spi.NoLogWebApplicationException; @@ -32,7 +33,6 @@ import org.keycloak.models.RealmModel; import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.representations.AccessToken; import org.keycloak.services.ForbiddenException; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.managers.AppAuthManager; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.RealmManager; @@ -61,7 +61,7 @@ import java.util.Properties; */ @Path("/admin") public class AdminRoot { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(AdminRoot.class); @Context protected UriInfo uriInfo; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java index b02dbb4899..a76783394b 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.keycloak.common.ClientConnection; import org.keycloak.events.admin.OperationType; @@ -24,7 +25,6 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserLoginFailureModel; import org.keycloak.models.UserModel; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.managers.BruteForceProtector; import javax.ws.rs.DELETE; @@ -46,7 +46,7 @@ import java.util.Map; * @version $Revision: 1 $ */ public class AttackDetectionResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(AttackDetectionResource.class); protected RealmAuth auth; protected RealmModel realm; private AdminEventBuilder adminEvent; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java index cc4605d4ca..087022fca3 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.BadRequestException; import org.jboss.resteasy.spi.NotFoundException; @@ -48,7 +49,6 @@ import org.keycloak.representations.idm.AuthenticatorConfigRepresentation; import org.keycloak.representations.idm.ConfigPropertyRepresentation; import org.keycloak.representations.idm.RequiredActionProviderRepresentation; import org.keycloak.services.ErrorResponse; -import org.keycloak.services.ServicesLogger; import org.keycloak.utils.CredentialHelper; import javax.ws.rs.Consumes; @@ -83,7 +83,7 @@ public class AuthenticationManagementResource { @Context private UriInfo uriInfo; - private static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(AuthenticationManagementResource.class); public AuthenticationManagementResource(RealmModel realm, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) { this.realm = realm; @@ -290,7 +290,7 @@ public class AuthenticationManagementResource { String newName = data.get("newName"); if (realm.getFlowByAlias(newName) != null) { - return Response.status(Response.Status.CONFLICT).build(); + return ErrorResponse.exists("New flow alias name already exists"); } AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias); @@ -310,7 +310,7 @@ public class AuthenticationManagementResource { data.put("id", copy.getId()); adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo).representation(data).success(); - return Response.status(201).build(); + return Response.status(Response.Status.CREATED).build(); } @@ -344,12 +344,12 @@ public class AuthenticationManagementResource { @POST @NoCache @Consumes(MediaType.APPLICATION_JSON) - public void addExecutionFlow(@PathParam("flowAlias") String flowAlias, Map data) { + public Response addExecutionFlow(@PathParam("flowAlias") String flowAlias, Map data) { auth.requireManage(); AuthenticationFlowModel parentFlow = realm.getFlowByAlias(flowAlias); if (parentFlow == null) { - throw new BadRequestException("Parent flow doesn't exists"); + return ErrorResponse.error("Parent flow doesn't exists", Response.Status.BAD_REQUEST); } String alias = data.get("alias"); String type = data.get("type"); @@ -359,7 +359,7 @@ public class AuthenticationManagementResource { AuthenticationFlowModel newFlow = realm.getFlowByAlias(alias); if (newFlow != null) { - throw new BadRequestException("New flow alias name already exists"); + return ErrorResponse.exists("New flow alias name already exists"); } newFlow = new AuthenticationFlowModel(); newFlow.setAlias(alias); @@ -377,6 +377,8 @@ public class AuthenticationManagementResource { data.put("id", execution.getId()); adminEvent.operation(OperationType.CREATE).resource(ResourceType.AUTH_EXECUTION_FLOW).resourcePath(uriInfo).representation(data).success(); + + return Response.status(Response.Status.CREATED).build(); } private int getNextPriority(AuthenticationFlowModel parentFlow) { diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java index 42de5f312e..a363e3906d 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.BadRequestException; import org.jboss.resteasy.spi.ResteasyProviderFactory; @@ -43,9 +44,7 @@ import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserSessionRepresentation; import org.keycloak.services.ErrorResponse; import org.keycloak.services.ErrorResponseException; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.clientregistration.ClientRegistrationTokenUtils; -import org.keycloak.services.clientregistration.RegistrationAccessToken; import org.keycloak.services.clientregistration.policy.RegistrationAuth; import org.keycloak.services.managers.ClientManager; import org.keycloak.services.managers.RealmManager; @@ -86,7 +85,7 @@ import static java.lang.Boolean.TRUE; * @version $Revision: 1 $ */ public class ClientResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(ClientResource.class); protected RealmModel realm; private RealmAuth auth; private AdminEventBuilder adminEvent; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java index 9b6c795a1e..7bdeccded2 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.events.admin.OperationType; @@ -29,7 +30,6 @@ import org.keycloak.models.RoleModel; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.services.ErrorResponseException; -import org.keycloak.services.ServicesLogger; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -53,7 +53,7 @@ import java.util.Set; * @version $Revision: 1 $ */ public class ClientRoleMappingsResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(ClientRoleMappingsResource.class); protected KeycloakSession session; protected RealmModel realm; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java index 761e307f9c..19b5e7d34b 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.ResteasyProviderFactory; @@ -30,8 +31,6 @@ import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.ClientTemplateRepresentation; import org.keycloak.services.ErrorResponse; -import org.keycloak.services.ServicesLogger; -import org.keycloak.services.resources.KeycloakApplication; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -52,7 +51,7 @@ import javax.ws.rs.core.UriInfo; * @version $Revision: 1 $ */ public class ClientTemplateResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(ClientTemplateResource.class); protected RealmModel realm; private RealmAuth auth; private AdminEventBuilder adminEvent; @@ -62,13 +61,6 @@ public class ClientTemplateResource { @Context protected UriInfo uriInfo; - @Context - protected KeycloakApplication keycloak; - - protected KeycloakApplication getKeycloakApplication() { - return keycloak; - } - public ClientTemplateResource(RealmModel realm, RealmAuth auth, ClientTemplateModel template, KeycloakSession session, AdminEventBuilder adminEvent) { this.realm = realm; this.auth = auth; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java index fb8a970376..d327fc3941 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.events.admin.OperationType; @@ -28,7 +29,6 @@ import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.ClientTemplateRepresentation; import org.keycloak.services.ErrorResponse; -import org.keycloak.services.ServicesLogger; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -50,7 +50,7 @@ import java.util.List; * @version $Revision: 1 $ */ public class ClientTemplatesResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(ClientTemplatesResource.class); protected RealmModel realm; private RealmAuth auth; private AdminEventBuilder adminEvent; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java index 237239ce80..2cd6d47530 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.events.admin.OperationType; @@ -28,7 +29,6 @@ import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.services.ErrorResponse; import org.keycloak.services.ErrorResponseException; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.managers.ClientManager; import org.keycloak.services.validation.ClientValidator; import org.keycloak.services.validation.PairwiseClientValidator; @@ -56,7 +56,7 @@ import java.util.Properties; * @version $Revision: 1 $ */ public class ClientsResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(ClientsResource.class); protected RealmModel realm; private RealmAuth auth; private AdminEventBuilder adminEvent; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java index 974dcf639f..39f152c2c4 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.common.ClientConnection; import org.keycloak.component.ComponentModel; @@ -27,7 +28,6 @@ import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.services.ErrorResponse; -import org.keycloak.services.ServicesLogger; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -52,7 +52,7 @@ import java.util.List; * @version $Revision: 1 $ */ public class ComponentResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(ComponentResource.class); protected RealmModel realm; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java index a34a98a9c5..a0e369f14a 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.broker.provider.IdentityProvider; @@ -41,7 +42,6 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.services.ErrorResponse; -import org.keycloak.services.ServicesLogger; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -67,7 +67,7 @@ import java.util.Map; */ public class IdentityProviderResource { - private static ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(IdentityProviderResource.class); private final RealmAuth auth; private final RealmModel realm; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java index 955fa13dba..e3765c9c5e 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.events.admin.OperationType; @@ -32,7 +33,6 @@ import org.keycloak.protocol.ProtocolMapperConfigException; import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.services.ErrorResponse; import org.keycloak.services.ErrorResponseException; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.resources.admin.RealmAuth.Resource; import javax.ws.rs.Consumes; @@ -59,7 +59,7 @@ import java.util.Properties; * @version $Revision: 1 $ */ public class ProtocolMappersResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(ProtocolMappersResource.class); protected RealmModel realm; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java index 4f06885b08..47db45e7bb 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.BadRequestException; import org.jboss.resteasy.spi.NotFoundException; @@ -54,24 +55,18 @@ import org.keycloak.provider.ProviderFactory; import org.keycloak.representations.adapters.action.GlobalRequestResult; import org.keycloak.representations.idm.AdminEventRepresentation; import org.keycloak.representations.idm.ClientRepresentation; -import org.keycloak.representations.idm.ComponentTypeRepresentation; import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.GroupRepresentation; import org.keycloak.representations.idm.PartialImportRepresentation; import org.keycloak.representations.idm.RealmEventsConfigRepresentation; import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.representations.info.SpiInfoRepresentation; import org.keycloak.services.ErrorResponse; -import org.keycloak.services.ServicesLogger; -import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy; -import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicySpi; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.LDAPConnectionTestManager; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.ResourceAdminManager; import org.keycloak.services.managers.UsersSyncManager; import org.keycloak.services.resources.admin.RealmAuth.Resource; -import org.keycloak.services.resources.admin.info.ServerInfoAdminResource; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -106,7 +101,7 @@ import java.util.regex.PatternSyntaxException; * @version $Revision: 1 $ */ public class RealmAdminResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(RealmAdminResource.class); protected RealmAuth auth; protected RealmModel realm; private TokenManager tokenManager; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java index 2437dd0c1e..ef72fa7662 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.ResteasyProviderFactory; @@ -31,7 +32,6 @@ import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.services.ErrorResponse; import org.keycloak.services.ForbiddenException; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resources.KeycloakApplication; @@ -58,7 +58,7 @@ import java.util.List; * @version $Revision: 1 $ */ public class RealmsAdminResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(RealmsAdminResource.class); protected AdminAuth auth; protected TokenManager tokenManager; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java index 345cae57fa..1b489331a0 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.events.admin.OperationType; @@ -26,7 +27,6 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; import org.keycloak.representations.idm.RoleRepresentation; -import org.keycloak.services.ServicesLogger; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -49,7 +49,7 @@ import java.util.Set; * @version $Revision: 1 $ */ public class RoleByIdResource extends RoleResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(RoleByIdResource.class); private final RealmModel realm; private final RealmAuth auth; private AdminEventBuilder adminEvent; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java index 91f3e950ad..28985c2c38 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.common.ClientConnection; @@ -32,7 +33,6 @@ import org.keycloak.representations.idm.ClientMappingsRepresentation; import org.keycloak.representations.idm.MappingsRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.services.ErrorResponseException; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.managers.RealmManager; import javax.ws.rs.Consumes; @@ -63,7 +63,7 @@ import java.util.Set; * @version $Revision: 1 $ */ public class RoleMapperResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + protected static final Logger logger = Logger.getLogger(RoleMapperResource.class); protected RealmModel realm; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java index e173cd366d..f8cb9e1a56 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.events.admin.OperationType; @@ -71,7 +72,7 @@ import java.util.Properties; */ public class UserFederationProviderResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(UserFederationProviderResource.class); private final KeycloakSession session; private final RealmModel realm; @@ -118,7 +119,7 @@ public class UserFederationProviderResource { new UsersSyncManager().notifyToRefreshPeriodicSync(session, realm, model, false); boolean kerberosCredsAdded = UserFederationProvidersResource.checkKerberosCredential(session, realm, model); if (kerberosCredsAdded) { - logger.addedKerberosToRealmCredentials(); + ServicesLogger.LOGGER.addedKerberosToRealmCredentials(); } adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success(); @@ -410,7 +411,7 @@ public class UserFederationProviderResource { UserFederationProviderFactory providerFactory = (UserFederationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, providerModel.getProviderName()); UserFederationProvider federationProvider = providerFactory.getInstance(session, providerModel); - logger.syncingDataForMapper(mapperModel.getName(), mapperModel.getFederationMapperType(), direction); + ServicesLogger.LOGGER.syncingDataForMapper(mapperModel.getName(), mapperModel.getFederationMapperType(), direction); UserFederationSyncResult syncResult; if ("fedToKeycloak".equals(direction)) { diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProvidersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProvidersResource.java index 6467dfd327..4333c93d92 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProvidersResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProvidersResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.ResteasyProviderFactory; @@ -67,7 +68,7 @@ import java.util.Properties; * @version $Revision: 1 $ */ public class UserFederationProvidersResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(UserFederationProvidersResource.class); protected RealmModel realm; @@ -208,7 +209,7 @@ public class UserFederationProvidersResource { new UsersSyncManager().notifyToRefreshPeriodicSync(session, realm, model, false); boolean kerberosCredsAdded = checkKerberosCredential(session, realm, model); if (kerberosCredsAdded) { - logger.addedKerberosToRealmCredentials(); + ServicesLogger.LOGGER.addedKerberosToRealmCredentials(); } rep.setId(model.getId()); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java index 1112416ccb..9fde51f7d8 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.common.ClientConnection; @@ -23,7 +24,6 @@ import org.keycloak.component.ComponentModel; import org.keycloak.events.admin.OperationType; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; -import org.keycloak.services.ServicesLogger; import org.keycloak.services.managers.UserStorageSyncManager; import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.UserStorageProviderModel; @@ -46,7 +46,7 @@ import java.util.Map; * @version $Revision: 1 $ */ public class UserStorageProviderResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(UserStorageProviderResource.class); protected RealmModel realm; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java index 138af53f35..f76761f0f4 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java @@ -16,6 +16,7 @@ */ package org.keycloak.services.resources.admin; +import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.BadRequestException; import org.jboss.resteasy.spi.NotFoundException; @@ -107,7 +108,7 @@ import java.util.concurrent.TimeUnit; * @version $Revision: 1 $ */ public class UsersResource { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(UsersResource.class); protected RealmModel realm; @@ -538,6 +539,8 @@ public class UsersResource { currentRep.put("grantedProtocolMappers", (rep==null ? Collections.emptyMap() : rep.getGrantedProtocolMappers())); currentRep.put("grantedRealmRoles", (rep==null ? Collections.emptyList() : rep.getGrantedRealmRoles())); currentRep.put("grantedClientRoles", (rep==null ? Collections.emptyMap() : rep.getGrantedClientRoles())); + currentRep.put("createdDate", (rep==null ? null : rep.getCreatedDate())); + currentRep.put("lastUpdatedDate", (rep==null ? null : rep.getLastUpdatedDate())); List> additionalGrants = new LinkedList<>(); if (hasOfflineToken) { @@ -857,7 +860,7 @@ public class UsersResource { return Response.ok().build(); } catch (EmailException e) { - logger.failedToSendActionsEmail(e); + ServicesLogger.LOGGER.failedToSendActionsEmail(e); return ErrorResponse.error("Failed to send execute actions email", Response.Status.INTERNAL_SERVER_ERROR); } } diff --git a/services/src/main/java/org/keycloak/services/scheduled/ClusterAwareScheduledTaskRunner.java b/services/src/main/java/org/keycloak/services/scheduled/ClusterAwareScheduledTaskRunner.java index da9c87670c..9b8bc409d8 100644 --- a/services/src/main/java/org/keycloak/services/scheduled/ClusterAwareScheduledTaskRunner.java +++ b/services/src/main/java/org/keycloak/services/scheduled/ClusterAwareScheduledTaskRunner.java @@ -17,6 +17,7 @@ package org.keycloak.services.scheduled; +import org.jboss.logging.Logger; import org.keycloak.cluster.ClusterProvider; import org.keycloak.cluster.ExecutionResult; import org.keycloak.models.KeycloakSession; @@ -32,6 +33,8 @@ import java.util.concurrent.Callable; */ public class ClusterAwareScheduledTaskRunner extends ScheduledTaskRunner { + private static final Logger logger = Logger.getLogger(ClusterAwareScheduledTaskRunner.class); + private final int intervalSecs; public ClusterAwareScheduledTaskRunner(KeycloakSessionFactory sessionFactory, ScheduledTask task, long intervalMillis) { diff --git a/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java b/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java index 81ff5f6ae2..3c3e8e2d58 100644 --- a/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java +++ b/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java @@ -17,6 +17,7 @@ package org.keycloak.services.scheduled; +import org.jboss.logging.Logger; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.services.ServicesLogger; @@ -27,7 +28,7 @@ import org.keycloak.timer.ScheduledTask; */ public class ScheduledTaskRunner implements Runnable { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(ScheduledTaskRunner.class); protected final KeycloakSessionFactory sessionFactory; protected final ScheduledTask task; @@ -43,14 +44,14 @@ public class ScheduledTaskRunner implements Runnable { try { runTask(session); } catch (Throwable t) { - logger.failedToRunScheduledTask(t, task.getClass().getSimpleName()); + ServicesLogger.LOGGER.failedToRunScheduledTask(t, task.getClass().getSimpleName()); session.getTransactionManager().rollback(); } finally { try { session.close(); } catch (Throwable t) { - logger.failedToCloseProviderSession(t); + ServicesLogger.LOGGER.failedToCloseProviderSession(t); } } } diff --git a/services/src/main/java/org/keycloak/transaction/JBossJtaTransactionManagerLookup.java b/services/src/main/java/org/keycloak/transaction/JBossJtaTransactionManagerLookup.java index 9d0be4a46e..2f3e895906 100644 --- a/services/src/main/java/org/keycloak/transaction/JBossJtaTransactionManagerLookup.java +++ b/services/src/main/java/org/keycloak/transaction/JBossJtaTransactionManagerLookup.java @@ -16,9 +16,9 @@ */ package org.keycloak.transaction; +import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.services.ServicesLogger; import javax.naming.InitialContext; import javax.naming.NamingException; @@ -29,7 +29,7 @@ import javax.transaction.TransactionManager; * @version $Revision: 1 $ */ public class JBossJtaTransactionManagerLookup implements JtaTransactionManagerLookup { - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(JBossJtaTransactionManagerLookup.class); private TransactionManager tm; @Override diff --git a/services/src/main/java/org/keycloak/utils/CredentialHelper.java b/services/src/main/java/org/keycloak/utils/CredentialHelper.java index 7db006ad56..b3f96785c0 100755 --- a/services/src/main/java/org/keycloak/utils/CredentialHelper.java +++ b/services/src/main/java/org/keycloak/utils/CredentialHelper.java @@ -17,6 +17,7 @@ package org.keycloak.utils; +import org.jboss.logging.Logger; import org.keycloak.authentication.Authenticator; import org.keycloak.authentication.AuthenticatorFactory; import org.keycloak.authentication.ClientAuthenticator; @@ -28,7 +29,6 @@ import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; -import org.keycloak.services.ServicesLogger; /** * used to set an execution a state based on type. @@ -38,7 +38,7 @@ import org.keycloak.services.ServicesLogger; */ public class CredentialHelper { - protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private static final Logger logger = Logger.getLogger(CredentialHelper.class); public static void setRequiredCredential(KeycloakSession session, String type, RealmModel realm) { AuthenticationExecutionModel.Requirement requirement = AuthenticationExecutionModel.Requirement.REQUIRED; diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml index d63f76be33..49c960495a 100644 --- a/testsuite/integration-arquillian/pom.xml +++ b/testsuite/integration-arquillian/pom.xml @@ -49,6 +49,10 @@ 2.2.2 1.0.0.Alpha2 + + 2.2.1.Final + 1.9.8.Final + 1.8 1.8 @@ -128,5 +132,20 @@ servers tests + + + + test-project-migration + + ${migration.project.version} + + + + test-product-migration + + ${migration.product.version} + + + diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/migration-strategy.xsl b/testsuite/integration-arquillian/servers/auth-server/jboss/common/migration-strategy.xsl new file mode 100644 index 0000000000..5bc33b86aa --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/migration-strategy.xsl @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml index e62c79e4b3..dc6587037d 100644 --- a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml @@ -571,6 +571,60 @@ + + + migration-manual + + + migration.mode + manual + + + + + + + org.codehaus.mojo + xml-maven-plugin + + + set-manual-migration-strategy + process-resources + + transform + + + + + + ${auth.server.home}/standalone/configuration + + standalone.xml + + ${common.resources}/migration-strategy.xsl + ${auth.server.home}/standalone/configuration + + + migration.strategy + manual + + + initialize.empty + false + + + + + + + + + + + + + + auth-server-apply-patch diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/components/TestImplProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/components/TestImplProviderFactory.java index 2a1a911aa2..4092501300 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/components/TestImplProviderFactory.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/components/TestImplProviderFactory.java @@ -36,12 +36,12 @@ import static org.keycloak.provider.ProviderConfigProperty.STRING_TYPE; public class TestImplProviderFactory implements TestProviderFactory { private List config = ProviderConfigurationBuilder.create() - .property("secret", "Secret", "A secret value", STRING_TYPE, null, true) - .property("number", "Number", "A number value", STRING_TYPE, null, false) - .property("required", "Required", "A required value", STRING_TYPE, null, false) - .property("val1", "Value 1", "Some more values", STRING_TYPE, null, false) - .property("val2", "Value 2", "Some more values", STRING_TYPE, null, false) - .property("val3", "Value 3", "Some more values", STRING_TYPE, null, false) + .property("secret", "Secret", "A secret value", STRING_TYPE, null, null, true) + .property("number", "Number", "A number value", STRING_TYPE, null, null, false) + .property("required", "Required", "A required value", STRING_TYPE, null, null, false) + .property("val1", "Value 1", "Some more values", STRING_TYPE, null, null, false) + .property("val2", "Value 2", "Some more values", STRING_TYPE, null, null, false) + .property("val3", "Value 3", "Some more values", STRING_TYPE, null, null, false) .build(); @Override diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java index 9542eaae29..bdeda57d90 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java @@ -64,7 +64,8 @@ public class AuthServerTestEnricher { private static final String AUTH_SERVER_CLUSTER_PROPERTY = "auth.server.cluster"; public static final boolean AUTH_SERVER_CLUSTER = Boolean.parseBoolean(System.getProperty(AUTH_SERVER_CLUSTER_PROPERTY, "false")); - private static final Boolean AUTO_MIGRATION_ENABLED = "auto".equals(System.getProperty("migration.mode")); + private static final Boolean START_MIGRATION_CONTAINER = "auto".equals(System.getProperty("migration.mode")) || + "manual".equals(System.getProperty("migration.mode")); @Inject @SuiteScoped @@ -130,7 +131,7 @@ public class AuthServerTestEnricher { throw new RuntimeException(String.format("No auth server container matching '%sN' found in arquillian.xml.", authServerBackend)); } - if (AUTO_MIGRATION_ENABLED) { + if (START_MIGRATION_CONTAINER) { // init migratedAuthServerInfo for (ContainerInfo container : suiteContext.getContainers()) { // migrated auth server diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java index d610957c29..fe3bce8269 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java @@ -49,9 +49,9 @@ import static org.keycloak.testsuite.util.IOUtil.loadJson; import static org.keycloak.testsuite.util.IOUtil.loadXML; import static org.keycloak.testsuite.util.IOUtil.modifyDocElementAttribute; import static org.keycloak.testsuite.util.IOUtil.modifyDocElementValue; -import static org.keycloak.testsuite.util.IOUtil.removeElementFromDoc; +import static org.keycloak.testsuite.util.IOUtil.removeElementsFromDoc; + -; /** * @author tkyjovsk @@ -97,7 +97,7 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor { if (archive.contains(adapterConfigPath)) { log.info("Modifying adapter config " + adapterConfigPath + " in " + archive.getName()); if (adapterConfigPath.equals(SAML_ADAPTER_CONFIG_PATH)) { // SAML adapter config - log.info("Modyfying saml adapter config in " + archive.getName()); + log.info("Modifying saml adapter config in " + archive.getName()); Document doc = loadXML(archive.get("WEB-INF/keycloak-saml.xml").getAsset().openStream()); if (authServerSslRequired) { @@ -148,7 +148,17 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor { } catch (IOException ex) { log.log(Level.FATAL, "Cannot serialize adapter config to JSON.", ex); } + + log.info("Adding OIDCFilter dependencies to " + archive.getName()); + ((WebArchive) archive).addAsLibraries(KeycloakDependenciesResolver.resolveDependencies("org.keycloak:keycloak-servlet-filter-adapter:" + System.getProperty("project.version"))); + } + + } else if (archive.getName().equals("customer-portal-subsystem.war")) { + + log.info("Adding OIDCFilter dependencies to " + archive.getName()); + ((WebArchive) archive).addAsLibraries(KeycloakDependenciesResolver.resolveDependencies("org.keycloak:keycloak-servlet-filter-adapter:" + System.getProperty("project.version"))); + } } @@ -210,9 +220,11 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor { appendChildInDocument(webXmlDoc, "web-app", filterMapping); //finally we need to remove all keycloak related configuration from web.xml - removeElementFromDoc(webXmlDoc, "web-app", "security-constraint"); - removeElementFromDoc(webXmlDoc, "web-app", "login-config"); - removeElementFromDoc(webXmlDoc, "web-app", "security-role"); + removeElementsFromDoc(webXmlDoc, "web-app", "security-constraint"); + removeElementsFromDoc(webXmlDoc, "web-app", "login-config"); + removeElementsFromDoc(webXmlDoc, "web-app", "security-role"); + + } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java index 015910bf4e..345e77eb8f 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java @@ -129,7 +129,7 @@ public class IOUtil { node.setTextContent(node.getTextContent().replace(regex, replacement)); } - public static void removeElementFromDoc(Document doc, String parentTag, String removeNode) { + public static void removeElementsFromDoc(Document doc, String parentTag, String removeNode) { NodeList nodes = doc.getElementsByTagName(parentTag); if (nodes.getLength() != 1) { log.warn("Not able or ambiguous to find element: " + parentTag); @@ -143,18 +143,23 @@ public class IOUtil { } NodeList removeNodes = parentElement.getElementsByTagName(removeNode); - if (removeNodes.getLength() != 1) { - log.warn("Not able or ambiguous to find element: " + removeNode + " within node " + parentTag); - return; - } - - Element removeElement = (Element) removeNodes.item(0); - if (removeElement == null) { + if (removeNodes == null) { log.warn("Not able to find element: " + removeNode + " within node " + parentTag); return; } - parentElement.removeChild(removeElement); + for (int i = 0; i < removeNodes.getLength();){ + Element removeElement = (Element) removeNodes.item(i); + if (removeElement == null) { + log.warn("Not able to find element: " + removeNode + " within node " + parentTag); + return; + } + + log.info("Removing node " + removeNode); + parentElement.removeChild(removeElement); + + } + } public static String getElementTextContent(Document doc, String path) { diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UserInfoClientUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UserInfoClientUtil.java index fc540231b4..fb39ec5457 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UserInfoClientUtil.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UserInfoClientUtil.java @@ -49,7 +49,7 @@ public class UserInfoClientUtil { return client.target(userInfoUri); } - public static void testSuccessfulUserInfoResponse(Response response, String expectedUsername, String expectedEmail) { + public static UserInfo testSuccessfulUserInfoResponse(Response response, String expectedUsername, String expectedEmail) { Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); Assert.assertEquals(response.getHeaderString(HttpHeaders.CONTENT_TYPE), MediaType.APPLICATION_JSON); @@ -61,6 +61,7 @@ public class UserInfoClientUtil { Assert.assertNotNull(userInfo.getSubject()); Assert.assertEquals(expectedEmail, userInfo.getEmail()); Assert.assertEquals(expectedUsername, userInfo.getPreferredUsername()); + return userInfo; } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java index 5bd733cfdf..676a40f052 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java @@ -278,6 +278,14 @@ public abstract class AbstractKeycloakTest { } } + /** + * Creates a user in the given realm and returns its ID. + * @param realm Realm name + * @param username Username + * @param password Password + * @param requiredActions + * @return ID of the newly created user + */ public String createUser(String realm, String username, String password, String ... requiredActions) { List requiredUserActions = Arrays.asList(requiredActions); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java index 63821b3bcc..4317317908 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java @@ -70,6 +70,7 @@ public abstract class AbstractAdapterTest extends AbstractAuthTest { modifyClientUrls(tr, "^(/.*)", appServerContextRootPage.toString() + "$1"); modifySamlMasterURLs(tr, "8080", System.getProperty("auth.server.http.port", null)); modifySAMLClientsAttributes(tr, "8080", System.getProperty("app.server.http.port", "8280")); + modifyClientJWKSUrl(tr, "^(/.*)", appServerContextRootPage.toString() + "$1"); } if ("true".equals(System.getProperty("auth.server.ssl.required"))) { tr.setSslRequired("all"); @@ -77,6 +78,16 @@ public abstract class AbstractAdapterTest extends AbstractAuthTest { } } + private void modifyClientJWKSUrl(RealmRepresentation realm, String regex, String replacement) { + if (realm.getClients() != null) { + realm.getClients().stream().filter(client -> "client-jwt".equals(client.getClientAuthenticatorType())).forEach(client -> { + Map attr = client.getAttributes(); + attr.put("jwks.url", attr.get("jwks.url").replaceFirst(regex, replacement)); + client.setAttributes(attr); + }); + } + } + public abstract void addAdapterTestRealms(List testRealms); public boolean isRelative() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoFilterServletAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoFilterServletAdapterTest.java new file mode 100644 index 0000000000..b53fdcc7bc --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoFilterServletAdapterTest.java @@ -0,0 +1,42 @@ +package org.keycloak.testsuite.adapter.servlet; + +import org.junit.Ignore; +import org.junit.Test; +import org.keycloak.testsuite.arquillian.annotation.UseServletFilter; + +/** + * Created by zschwarz on 9/14/16. + */ + +@UseServletFilter(filterName = "oidc-filter", filterClass = "org.keycloak.adapters.servlet.KeycloakOIDCFilter") +public abstract class AbstractDemoFilterServletAdapterTest extends AbstractDemoServletsAdapterTest { + + + @Test + @Override + @Ignore + public void testCustomerPortalWithSubsystemSettings() { + + } + + @Test + @Override + @Ignore + public void testAuthenticated() { + + } + + @Test + @Override + @Ignore + public void testOIDCParamsForwarding() { + + } + + @Test + @Override + @Ignore + public void testClientWithJwksUri() { + + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ComponentsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ComponentsTest.java index 07c5bebf58..ab934fe8d3 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ComponentsTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ComponentsTest.java @@ -20,25 +20,13 @@ package org.keycloak.testsuite.admin; import org.junit.Before; import org.junit.Test; import org.keycloak.admin.client.resource.ComponentsResource; -import org.keycloak.common.util.CertificateUtils; -import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.MultivaluedHashMap; -import org.keycloak.common.util.PemUtils; -import org.keycloak.keys.Attributes; -import org.keycloak.keys.KeyProvider; -import org.keycloak.keys.RsaKeyProviderFactory; import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.representations.idm.ErrorRepresentation; -import org.keycloak.representations.idm.KeysMetadataRepresentation; -import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.testsuite.components.TestImplProviderFactory; import org.keycloak.testsuite.components.TestProvider; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; -import java.math.BigInteger; -import java.security.KeyPair; -import java.security.PublicKey; import java.util.Collections; import java.util.List; import java.util.Map; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java index f44cc2e668..7e61f511db 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java @@ -45,20 +45,30 @@ import javax.ws.rs.core.Response; import java.io.IOException; import java.net.URI; import java.util.Collections; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; -import java.util.Map; +import java.util.UUID; +import javax.ws.rs.ClientErrorException; +import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; +import org.junit.Rule; +import org.junit.rules.ExpectedException; +import org.keycloak.admin.client.Keycloak; +import org.keycloak.models.AdminRoles; import static org.keycloak.testsuite.Assert.assertNames; +import org.keycloak.testsuite.arquillian.AuthServerTestEnricher; +import org.keycloak.testsuite.auth.page.AuthRealm; +import org.keycloak.testsuite.util.GroupBuilder; /** * @author Marko Strukelj */ public class GroupTest extends AbstractGroupTest { + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Override public void addTestRealms(List testRealms) { RealmRepresentation testRealmRep = loadTestRealm(testRealms); @@ -293,26 +303,24 @@ public class GroupTest extends AbstractGroupTest { @Test public void updateGroup() { RealmResource realm = adminClient.realms().realm("test"); + final String groupName = "group-" + UUID.randomUUID(); - GroupRepresentation group = new GroupRepresentation(); - group.setName("group"); - - Map> attrs = new HashMap<>(); - attrs.put("attr1", Collections.singletonList("attrval1")); - attrs.put("attr2", Collections.singletonList("attrval2")); - group.setAttributes(attrs); + GroupRepresentation group = GroupBuilder.create() + .name(groupName) + .singleAttribute("attr1", "attrval1") + .singleAttribute("attr2", "attrval2") + .build(); createGroup(realm, group); - group = realm.getGroupByPath("/group"); + group = realm.getGroupByPath("/" + groupName); Assert.assertNotNull(group); - assertEquals("group", group.getName()); - assertEquals(2, group.getAttributes().size()); - assertEquals(1, group.getAttributes().get("attr1").size()); - assertEquals("attrval1", group.getAttributes().get("attr1").get(0)); - assertEquals(1, group.getAttributes().get("attr2").size()); - assertEquals("attrval2", group.getAttributes().get("attr2").get(0)); + assertThat(group.getName(), is(groupName)); + assertThat(group.getAttributes().keySet(), containsInAnyOrder("attr1", "attr2")); + assertThat(group.getAttributes(), hasEntry(is("attr1"), contains("attrval1"))); + assertThat(group.getAttributes(), hasEntry(is("attr2"), contains("attrval2"))); - group.setName("group-new"); + final String groupNewName = "group-" + UUID.randomUUID(); + group.setName(groupNewName); group.getAttributes().remove("attr1"); group.getAttributes().get("attr2").add("attrval2-2"); @@ -321,12 +329,12 @@ public class GroupTest extends AbstractGroupTest { realm.groups().group(group.getId()).update(group); assertAdminEvents.assertEvent("test", OperationType.UPDATE, AdminEventPaths.groupPath(group.getId()), group, ResourceType.GROUP); - group = realm.getGroupByPath("/group-new"); + group = realm.getGroupByPath("/" + groupNewName); - assertEquals("group-new", group.getName()); - assertEquals(2, group.getAttributes().size()); - assertEquals(2, group.getAttributes().get("attr2").size()); - assertEquals(1, group.getAttributes().get("attr3").size()); + assertThat(group.getName(), is(groupNewName)); + assertThat(group.getAttributes().keySet(), containsInAnyOrder("attr2", "attr3")); + assertThat(group.getAttributes(), hasEntry(is("attr2"), containsInAnyOrder("attrval2", "attrval2-2"))); + assertThat(group.getAttributes(), hasEntry(is("attr3"), contains("attrval2"))); } @Test @@ -457,4 +465,117 @@ public class GroupTest extends AbstractGroupTest { assertNames(roles.clientLevel(clientId).listAll(), "client-composite"); } + + /** + * Verifies that the user does not have access to Keycloak Admin endpoint when role is not + * assigned to that user. + * @link https://issues.jboss.org/browse/KEYCLOAK-2964 + */ + @Test + public void noAdminEndpointAccessWhenNoRoleAssigned() { + String userName = "user-" + UUID.randomUUID(); + final String realmName = AuthRealm.MASTER; + createUser(realmName, userName, "pwd"); + + Keycloak userClient = Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth", + realmName, userName, "pwd", Constants.ADMIN_CLI_CLIENT_ID); + + expectedException.expect(ClientErrorException.class); + expectedException.expectMessage(String.valueOf(Response.Status.FORBIDDEN.getStatusCode())); + userClient.realms().findAll(); // Any admin operation will do + } + + /** + * Verifies that the role assigned to a user is correctly handled by Keycloak Admin endpoint. + * @link https://issues.jboss.org/browse/KEYCLOAK-2964 + */ + @Test + public void adminEndpointAccessibleWhenAdminRoleAssignedToUser() { + String userName = "user-" + UUID.randomUUID(); + + final String realmName = AuthRealm.MASTER; + RealmResource realm = adminClient.realms().realm(realmName); + RoleRepresentation adminRole = realm.roles().get(AdminRoles.ADMIN).toRepresentation(); + assertThat(adminRole, notNullValue()); + assertThat(adminRole.getId(), notNullValue()); + + String userId = createUser(realmName, userName, "pwd"); + assertThat(userId, notNullValue()); + + RoleMappingResource mappings = realm.users().get(userId).roles(); + mappings.realmLevel().add(Collections.singletonList(adminRole)); + + Keycloak userClient = Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth", + realmName, userName, "pwd", Constants.ADMIN_CLI_CLIENT_ID); + + assertThat(userClient.realms().findAll(), // Any admin operation will do + not(empty())); + } + + /** + * Verifies that the role assigned to a user's group is correctly handled by Keycloak Admin endpoint. + * @link https://issues.jboss.org/browse/KEYCLOAK-2964 + */ + @Test + public void adminEndpointAccessibleWhenAdminRoleAssignedToGroup() { + String userName = "user-" + UUID.randomUUID(); + String groupName = "group-" + UUID.randomUUID(); + + final String realmName = AuthRealm.MASTER; + RealmResource realm = adminClient.realms().realm(realmName); + RoleRepresentation adminRole = realm.roles().get(AdminRoles.ADMIN).toRepresentation(); + assertThat(adminRole, notNullValue()); + assertThat(adminRole.getId(), notNullValue()); + + String userId = createUser(realmName, userName, "pwd"); + GroupRepresentation group = GroupBuilder.create().name(groupName).build(); + Response response = realm.groups().add(group); + String groupId = ApiUtil.getCreatedId(response); + response.close(); + + RoleMappingResource mappings = realm.groups().group(groupId).roles(); + mappings.realmLevel().add(Collections.singletonList(adminRole)); + + realm.users().get(userId).joinGroup(groupId); + + Keycloak userClient = Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth", + realmName, userName, "pwd", Constants.ADMIN_CLI_CLIENT_ID); + + assertThat(userClient.realms().findAll(), // Any admin operation will do + not(empty())); + } + + + /** + * Verifies that the role assigned to a user's group is correctly handled by Keycloak Admin endpoint. + * @link https://issues.jboss.org/browse/KEYCLOAK-2964 + */ + @Test + public void adminEndpointAccessibleWhenAdminRoleAssignedToGroupAfterUserJoinedIt() { + String userName = "user-" + UUID.randomUUID(); + String groupName = "group-" + UUID.randomUUID(); + + final String realmName = AuthRealm.MASTER; + RealmResource realm = adminClient.realms().realm(realmName); + RoleRepresentation adminRole = realm.roles().get(AdminRoles.ADMIN).toRepresentation(); + assertThat(adminRole, notNullValue()); + assertThat(adminRole.getId(), notNullValue()); + + String userId = createUser(realmName, userName, "pwd"); + GroupRepresentation group = GroupBuilder.create().name(groupName).build(); + Response response = realm.groups().add(group); + String groupId = ApiUtil.getCreatedId(response); + response.close(); + + realm.users().get(userId).joinGroup(groupId); + + RoleMappingResource mappings = realm.groups().group(groupId).roles(); + mappings.realmLevel().add(Collections.singletonList(adminRole)); + + Keycloak userClient = Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth", + realmName, userName, "pwd", Constants.ADMIN_CLI_CLIENT_ID); + + assertThat(userClient.realms().findAll(), // Any admin operation will do + not(empty())); + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationPoliciesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationPoliciesTest.java similarity index 99% rename from testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationPoliciesTest.java rename to testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationPoliciesTest.java index 12d24ed65d..2a4ef8b7e6 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationPoliciesTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationPoliciesTest.java @@ -63,7 +63,7 @@ import static org.junit.Assert.assertTrue; /** * @author Marek Posolda */ -public class OIDCClientRegistrationPoliciesTest extends AbstractClientRegistrationTest { +public class ClientRegistrationPoliciesTest extends AbstractClientRegistrationTest { private static final String PRIVATE_KEY = "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y="; private static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"; @@ -329,7 +329,7 @@ public class OIDCClientRegistrationPoliciesTest extends AbstractClientRegistrati ConfigPropertyRepresentation allowedProtocolMappers = list.get(0); Assert.assertEquals(allowedProtocolMappers.getName(), expectedConfigPropName); - return (List) allowedProtocolMappers.getDefaultValue(); + return allowedProtocolMappers.getOptions(); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java index ca4553d69e..c5bb78577b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java @@ -27,6 +27,7 @@ import org.keycloak.client.registration.ClientRegistrationException; import org.keycloak.client.registration.HttpErrorException; import org.keycloak.protocol.oidc.mappers.SHA256PairwiseSubMapper; import org.keycloak.representations.AccessToken; +import org.keycloak.representations.UserInfo; import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation; import org.keycloak.representations.idm.ClientInitialAccessPresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; @@ -38,7 +39,9 @@ import org.keycloak.testsuite.client.resources.TestApplicationResourceUrls; import org.keycloak.testsuite.client.resources.TestOIDCEndpointsApplicationResource; import org.keycloak.testsuite.util.ClientManager; import org.keycloak.testsuite.util.OAuthClient; +import org.keycloak.testsuite.util.UserInfoClientUtil; +import javax.ws.rs.client.Client; import javax.ws.rs.core.Response; import java.util.ArrayList; import java.util.Collections; @@ -323,5 +326,17 @@ public class OIDCPairwiseClientRegistrationTest extends AbstractClientRegistrati // Assert pairwise client has different subject like userId String pairwiseUserId = accessToken.getSubject(); Assert.assertNotEquals(pairwiseUserId, user.getId()); + + // Send request to userInfo endpoint + Client jaxrsClient = javax.ws.rs.client.ClientBuilder.newClient(); + try { + // Check that userInfo contains pairwise subjectId as well + Response userInfoResponse = UserInfoClientUtil.executeUserInfoRequest_getMethod(jaxrsClient, accessTokenResponse.getAccessToken()); + UserInfo userInfo = UserInfoClientUtil.testSuccessfulUserInfoResponse(userInfoResponse, "test-user", "test-user@localhost"); + String userInfoSubId = userInfo.getSubject(); + Assert.assertEquals(pairwiseUserId, userInfoSubId); + } finally { + jaxrsClient.close(); + } } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java index 9889d1307e..d9c041c14d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java @@ -36,10 +36,8 @@ import org.keycloak.models.utils.DefaultAuthenticationFlows; import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation; import org.keycloak.representations.idm.AuthenticationFlowRepresentation; import org.keycloak.representations.idm.ClientRepresentation; -import org.keycloak.representations.idm.GroupRepresentation; import org.keycloak.representations.idm.RequiredActionProviderRepresentation; import org.keycloak.representations.idm.RoleRepresentation; -import org.keycloak.representations.idm.UserRepresentation; import static org.keycloak.testsuite.Assert.*; import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER; @@ -49,6 +47,8 @@ import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER; public class MigrationTest extends AbstractKeycloakTest { private final String MIGRATION = "Migration"; + private RealmResource migrationRealm; + private RealmResource masterRealm; @Override public void addTestRealms(List testRealms) { @@ -57,66 +57,74 @@ public class MigrationTest extends AbstractKeycloakTest { @Before public void beforeMigrationTest() { + migrationRealm = adminClient.realms().realm(MIGRATION); + masterRealm = adminClient.realms().realm(MASTER); + //add migration realm to testRealmReps to make the migration removed after test testRealmReps.add(adminClient.realms().realm(MIGRATION).toRepresentation()); } @Test @Migration(versionFrom = "1.9.8.Final") - public void migration198Test() { - RealmResource migrationRealm = adminClient.realms().realm(MIGRATION); - RealmResource masterRealm = adminClient.realms().realm(MASTER); - - testMigratedMasterData(masterRealm); - testMigratedMigrationData(migrationRealm); - - // 2.0.0 - org.keycloak.migration.migrators.MigrateTo2_0_0 - testAuthorizationServices(masterRealm, migrationRealm); - - // 2.1.0 - org.keycloak.migration.migrators.MigrateTo2_1_0 - testNameOfOTPRequiredAction(masterRealm, migrationRealm); - //there is no migration of RolePolicies (MigrateTo2_1_0#migrateRolePolicies) between 1.9.8 to current version (2.3.0-SNAPSHOT) - - // 2.2.0 - org.keycloak.migration.migrators.MigrateTo2_2_0 - testIdentityProviderAuthenticator(masterRealm, migrationRealm); + public void migration1_9_8Test() { + testMigratedData(); + testMigrationTo2_0_0(); + testMigrationTo2_1_0(); + testMigrationTo2_2_0(); } @Test @Migration(versionFrom = "2.2.1.Final") - public void migration221Test() { - RealmResource migrationRealm = adminClient.realms().realm(MIGRATION); - RealmResource masterRealm = adminClient.realms().realm(MASTER); - - testMigratedMasterData(masterRealm); - testMigratedMigrationData(migrationRealm); - - // so far nothing else + public void migration2_2_1Test() { + testMigratedData(); } - private void testMigratedMasterData(RealmResource master) { - assertNames(master.roles().list(), "offline_access", "uma_authorization", "create-realm", "master-test-realm-role", "admin"); - assertNames(master.clients().findAll(), "admin-cli", "security-admin-console", "broker", "account", + private void testMigratedData() { + //master realm + assertNames(masterRealm.roles().list(), "offline_access", "uma_authorization", "create-realm", "master-test-realm-role", "admin"); + assertNames(masterRealm.clients().findAll(), "admin-cli", "security-admin-console", "broker", "account", "master-realm", "master-test-client", "Migration-realm"); - String id = master.clients().findByClientId("master-test-client").get(0).getId(); - assertNames(master.clients().get(id).roles().list(), "master-test-client-role"); - assertNames(master.users().search("", 0, 5), "admin", "master-test-user"); - assertNames(master.groups().groups(), "master-test-group"); + String id = masterRealm.clients().findByClientId("master-test-client").get(0).getId(); + assertNames(masterRealm.clients().get(id).roles().list(), "master-test-client-role"); + assertNames(masterRealm.users().search("", 0, 5), "admin", "master-test-user"); + assertNames(masterRealm.groups().groups(), "master-test-group"); + + //migrationRealm + assertNames(migrationRealm.roles().list(), "offline_access", "uma_authorization", "migration-test-realm-role"); + assertNames(migrationRealm.clients().findAll(), "account", "admin-cli", "broker", "migration-test-client", "realm-management", "security-admin-console"); + String id2 = migrationRealm.clients().findByClientId("migration-test-client").get(0).getId(); + assertNames(migrationRealm.clients().get(id2).roles().list(), "migration-test-client-role"); + assertNames(migrationRealm.users().search("", 0, 5), "migration-test-user"); + assertNames(migrationRealm.groups().groups(), "migration-test-group"); } - private void testMigratedMigrationData(RealmResource migration) { - assertNames(migration.roles().list(), "offline_access", "uma_authorization", "migration-test-realm-role"); - assertNames(migration.clients().findAll(), "account", "admin-cli", "broker", "migration-test-client", "realm-management", "security-admin-console"); - String id = migration.clients().findByClientId("migration-test-client").get(0).getId(); - assertNames(migration.clients().get(id).roles().list(), "migration-test-client-role"); - assertNames(migration.users().search("", 0, 5), "migration-test-user"); - assertNames(migration.groups().groups(), "migration-test-group"); + /** + * @see org.keycloak.migration.migrators.MigrateTo2_0_0 + */ + private void testMigrationTo2_0_0() { + testAuthorizationServices(masterRealm, migrationRealm); + } + + /** + * @see org.keycloak.migration.migrators.MigrateTo2_1_0 + */ + private void testMigrationTo2_1_0() { + testNameOfOTPRequiredAction(masterRealm, migrationRealm); + } + + /** + * @see org.keycloak.migration.migrators.MigrateTo2_2_0 + */ + private void testMigrationTo2_2_0() { + testIdentityProviderAuthenticator(masterRealm, migrationRealm); + //MigrateTo2_2_0#migrateRolePolicies is not relevant any more } private void testAuthorizationServices(RealmResource... realms) { for (RealmResource realm : realms) { //test setup of authorization services for (String roleName : Constants.AUTHZ_DEFAULT_AUTHORIZATION_ROLES) { - RoleResource role = realm.roles().get(roleName); //throw javax.ws.rs.NotFoundException + RoleResource role = realm.roles().get(roleName); //throws javax.ws.rs.NotFoundException if not found assertFalse("Role's scopeParamRequired should be false.", role.toRepresentation().isScopeParamRequired()); assertFalse("Role shouldn't be composite should be false.", role.toRepresentation().isComposite()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java index 13c1ea45ad..eaeeafd9c1 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java @@ -673,138 +673,6 @@ public class AccessTokenTest extends AbstractKeycloakTest { } - @Test - public void testTokenMapping() throws Exception { - Client client = javax.ws.rs.client.ClientBuilder.newClient(); - UriBuilder builder = UriBuilder.fromUri(AUTH_SERVER_ROOT); - URI grantUri = OIDCLoginProtocolService.tokenUrl(builder).build("test"); - WebTarget grantTarget = client.target(grantUri); - { - UserResource userResource = findUserByUsernameId(adminClient.realm("test"), "test-user@localhost"); - UserRepresentation user = userResource.toRepresentation(); - - user.singleAttribute("street", "5 Yawkey Way"); - user.singleAttribute("locality", "Boston"); - user.singleAttribute("region", "MA"); - user.singleAttribute("postal_code", "02115"); - user.singleAttribute("country", "USA"); - user.singleAttribute("phone", "617-777-6666"); - - List departments = Arrays.asList("finance", "development"); - user.getAttributes().put("departments", departments); - userResource.update(user); - - ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app"); - - ProtocolMapperRepresentation mapper = createAddressMapper(true, true); - app.getProtocolMappers().createMapper(mapper); - - ProtocolMapperRepresentation hard = createHardcodedClaim("hard", "hard", "coded", "String", false, null, true, true); - app.getProtocolMappers().createMapper(hard); - app.getProtocolMappers().createMapper(createHardcodedClaim("hard-nested", "nested.hard", "coded-nested", "String", false, null, true, true)); - app.getProtocolMappers().createMapper(createClaimMapper("custom phone", "phone", "home_phone", "String", true, "", true, true, false)); - app.getProtocolMappers().createMapper(createClaimMapper("nested phone", "phone", "home.phone", "String", true, "", true, true, false)); - app.getProtocolMappers().createMapper(createClaimMapper("departments", "departments", "department", "String", true, "", true, true, true)); - app.getProtocolMappers().createMapper(createHardcodedRole("hard-realm", "hardcoded")); - app.getProtocolMappers().createMapper(createHardcodedRole("hard-app", "app.hardcoded")); - app.getProtocolMappers().createMapper(createRoleNameMapper("rename-app-role", "test-app.customer-user", "realm-user")); - } - - { - Response response = executeGrantAccessTokenRequest(grantTarget); - assertEquals(200, response.getStatus()); - - org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class); - IDToken idToken = getIdToken(tokenResponse); - assertNotNull(idToken.getAddress()); - assertEquals(idToken.getName(), "Tom Brady"); - assertEquals(idToken.getAddress().getStreetAddress(), "5 Yawkey Way"); - assertEquals(idToken.getAddress().getLocality(), "Boston"); - assertEquals(idToken.getAddress().getRegion(), "MA"); - assertEquals(idToken.getAddress().getPostalCode(), "02115"); - assertEquals(idToken.getAddress().getCountry(), "USA"); - assertNotNull(idToken.getOtherClaims().get("home_phone")); - assertEquals("617-777-6666", idToken.getOtherClaims().get("home_phone")); - assertEquals("coded", idToken.getOtherClaims().get("hard")); - Map nested = (Map) idToken.getOtherClaims().get("nested"); - assertEquals("coded-nested", nested.get("hard")); - nested = (Map) idToken.getOtherClaims().get("home"); - assertEquals("617-777-6666", nested.get("phone")); - List departments = (List) idToken.getOtherClaims().get("department"); - assertEquals(2, departments.size()); - assertTrue(departments.contains("finance") && departments.contains("development")); - - AccessToken accessToken = getAccessToken(tokenResponse); - assertEquals(accessToken.getName(), "Tom Brady"); - assertNotNull(accessToken.getAddress()); - assertEquals(accessToken.getAddress().getStreetAddress(), "5 Yawkey Way"); - assertEquals(accessToken.getAddress().getLocality(), "Boston"); - assertEquals(accessToken.getAddress().getRegion(), "MA"); - assertEquals(accessToken.getAddress().getPostalCode(), "02115"); - assertEquals(accessToken.getAddress().getCountry(), "USA"); - assertNotNull(accessToken.getOtherClaims().get("home_phone")); - assertEquals("617-777-6666", accessToken.getOtherClaims().get("home_phone")); - assertEquals("coded", accessToken.getOtherClaims().get("hard")); - nested = (Map) accessToken.getOtherClaims().get("nested"); - assertEquals("coded-nested", nested.get("hard")); - nested = (Map) accessToken.getOtherClaims().get("home"); - assertEquals("617-777-6666", nested.get("phone")); - departments = (List) idToken.getOtherClaims().get("department"); - assertEquals(2, departments.size()); - assertTrue(departments.contains("finance") && departments.contains("development")); - assertTrue(accessToken.getRealmAccess().getRoles().contains("hardcoded")); - assertTrue(accessToken.getRealmAccess().getRoles().contains("realm-user")); - Assert.assertFalse(accessToken.getResourceAccess("test-app").getRoles().contains("customer-user")); - assertTrue(accessToken.getResourceAccess("app").getRoles().contains("hardcoded")); - - - response.close(); - } - - // undo mappers - { - ClientResource app = findClientByClientId(adminClient.realm("test"), "test-app"); - ClientRepresentation clientRepresentation = app.toRepresentation(); - for (ProtocolMapperRepresentation model : clientRepresentation.getProtocolMappers()) { - if (model.getName().equals("address") - || model.getName().equals("hard") - || model.getName().equals("hard-nested") - || model.getName().equals("custom phone") - || model.getName().equals("departments") - || model.getName().equals("nested phone") - || model.getName().equals("rename-app-role") - || model.getName().equals("hard-realm") - || model.getName().equals("hard-app") - ) { - app.getProtocolMappers().delete(model.getId()); - } - } - } - - events.clear(); - - - { - Response response = executeGrantAccessTokenRequest(grantTarget); - assertEquals(200, response.getStatus()); - org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class); - IDToken idToken = getIdToken(tokenResponse); - assertNull(idToken.getAddress()); - assertNull(idToken.getOtherClaims().get("home_phone")); - assertNull(idToken.getOtherClaims().get("hard")); - assertNull(idToken.getOtherClaims().get("nested")); - assertNull(idToken.getOtherClaims().get("department")); - - response.close(); - } - - - events.clear(); - client.close(); - - - } - @Test public void testClientTemplate() throws Exception { RealmResource realm = adminClient.realm("test"); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OIDCProtocolMappersTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OIDCProtocolMappersTest.java new file mode 100644 index 0000000000..d317af7de1 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OIDCProtocolMappersTest.java @@ -0,0 +1,244 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed 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. + */ + +package org.keycloak.testsuite.oauth; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.keycloak.admin.client.resource.ClientResource; +import org.keycloak.admin.client.resource.ProtocolMappersResource; +import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.representations.AccessToken; +import org.keycloak.representations.IDToken; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.ProtocolMapperRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testsuite.AbstractKeycloakTest; +import org.keycloak.testsuite.Assert; +import org.keycloak.testsuite.AssertEvents; +import org.keycloak.testsuite.admin.ApiUtil; +import org.keycloak.testsuite.util.ClientManager; +import org.keycloak.testsuite.util.OAuthClient; +import org.keycloak.testsuite.util.ProtocolMapperUtil; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson; +import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId; +import static org.keycloak.testsuite.admin.ApiUtil.findClientResourceByClientId; +import static org.keycloak.testsuite.admin.ApiUtil.findUserByUsernameId; +import static org.keycloak.testsuite.util.ProtocolMapperUtil.createAddressMapper; +import static org.keycloak.testsuite.util.ProtocolMapperUtil.createClaimMapper; +import static org.keycloak.testsuite.util.ProtocolMapperUtil.createHardcodedClaim; +import static org.keycloak.testsuite.util.ProtocolMapperUtil.createHardcodedRole; +import static org.keycloak.testsuite.util.ProtocolMapperUtil.createRoleNameMapper; + +/** + * @author Marek Posolda + */ +public class OIDCProtocolMappersTest extends AbstractKeycloakTest { + + @Rule + public AssertEvents events = new AssertEvents(this); + + + @Override + public void beforeAbstractKeycloakTest() throws Exception { + super.beforeAbstractKeycloakTest(); + } + + @Before + public void clientConfiguration() { + ClientManager.realm(adminClient.realm("test")).clientId("test-app").directAccessGrant(true); + /* + * Configure the default client ID. Seems like OAuthClient is keeping the state of clientID + * For example: If some test case configure oauth.clientId("sample-public-client"), other tests + * will faile and the clientID will always be "sample-public-client + * @see AccessTokenTest#testAuthorizationNegotiateHeaderIgnored() + */ + oauth.clientId("test-app"); + } + + @Override + public void addTestRealms(List testRealms) { + RealmRepresentation realm = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class); + testRealms.add(realm); + } + + + @Test + public void testTokenMapping() throws Exception { + { + UserResource userResource = findUserByUsernameId(adminClient.realm("test"), "test-user@localhost"); + UserRepresentation user = userResource.toRepresentation(); + + user.singleAttribute("street", "5 Yawkey Way"); + user.singleAttribute("locality", "Boston"); + user.singleAttribute("region", "MA"); + user.singleAttribute("postal_code", "02115"); + user.singleAttribute("country", "USA"); + user.singleAttribute("phone", "617-777-6666"); + + List departments = Arrays.asList("finance", "development"); + user.getAttributes().put("departments", departments); + userResource.update(user); + + ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app"); + + ProtocolMapperRepresentation mapper = createAddressMapper(true, true); + app.getProtocolMappers().createMapper(mapper); + + ProtocolMapperRepresentation hard = createHardcodedClaim("hard", "hard", "coded", "String", false, null, true, true); + app.getProtocolMappers().createMapper(hard); + app.getProtocolMappers().createMapper(createHardcodedClaim("hard-nested", "nested.hard", "coded-nested", "String", false, null, true, true)); + app.getProtocolMappers().createMapper(createClaimMapper("custom phone", "phone", "home_phone", "String", true, "", true, true, false)); + app.getProtocolMappers().createMapper(createClaimMapper("nested phone", "phone", "home.phone", "String", true, "", true, true, false)); + app.getProtocolMappers().createMapper(createClaimMapper("departments", "departments", "department", "String", true, "", true, true, true)); + app.getProtocolMappers().createMapper(createHardcodedRole("hard-realm", "hardcoded")); + app.getProtocolMappers().createMapper(createHardcodedRole("hard-app", "app.hardcoded")); + app.getProtocolMappers().createMapper(createRoleNameMapper("rename-app-role", "test-app.customer-user", "realm-user")); + } + + { + OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password"); + + IDToken idToken = oauth.verifyIDToken(response.getIdToken()); + assertNotNull(idToken.getAddress()); + assertEquals(idToken.getName(), "Tom Brady"); + assertEquals(idToken.getAddress().getStreetAddress(), "5 Yawkey Way"); + assertEquals(idToken.getAddress().getLocality(), "Boston"); + assertEquals(idToken.getAddress().getRegion(), "MA"); + assertEquals(idToken.getAddress().getPostalCode(), "02115"); + assertEquals(idToken.getAddress().getCountry(), "USA"); + assertNotNull(idToken.getOtherClaims().get("home_phone")); + assertEquals("617-777-6666", idToken.getOtherClaims().get("home_phone")); + assertEquals("coded", idToken.getOtherClaims().get("hard")); + Map nested = (Map) idToken.getOtherClaims().get("nested"); + assertEquals("coded-nested", nested.get("hard")); + nested = (Map) idToken.getOtherClaims().get("home"); + assertEquals("617-777-6666", nested.get("phone")); + List departments = (List) idToken.getOtherClaims().get("department"); + assertEquals(2, departments.size()); + assertTrue(departments.contains("finance") && departments.contains("development")); + + AccessToken accessToken = oauth.verifyToken(response.getAccessToken()); + assertEquals(accessToken.getName(), "Tom Brady"); + assertNotNull(accessToken.getAddress()); + assertEquals(accessToken.getAddress().getStreetAddress(), "5 Yawkey Way"); + assertEquals(accessToken.getAddress().getLocality(), "Boston"); + assertEquals(accessToken.getAddress().getRegion(), "MA"); + assertEquals(accessToken.getAddress().getPostalCode(), "02115"); + assertEquals(accessToken.getAddress().getCountry(), "USA"); + assertNotNull(accessToken.getOtherClaims().get("home_phone")); + assertEquals("617-777-6666", accessToken.getOtherClaims().get("home_phone")); + assertEquals("coded", accessToken.getOtherClaims().get("hard")); + nested = (Map) accessToken.getOtherClaims().get("nested"); + assertEquals("coded-nested", nested.get("hard")); + nested = (Map) accessToken.getOtherClaims().get("home"); + assertEquals("617-777-6666", nested.get("phone")); + departments = (List) idToken.getOtherClaims().get("department"); + assertEquals(2, departments.size()); + assertTrue(departments.contains("finance") && departments.contains("development")); + assertTrue(accessToken.getRealmAccess().getRoles().contains("hardcoded")); + assertTrue(accessToken.getRealmAccess().getRoles().contains("realm-user")); + Assert.assertFalse(accessToken.getResourceAccess("test-app").getRoles().contains("customer-user")); + assertTrue(accessToken.getResourceAccess("app").getRoles().contains("hardcoded")); + } + + // undo mappers + { + ClientResource app = findClientByClientId(adminClient.realm("test"), "test-app"); + ClientRepresentation clientRepresentation = app.toRepresentation(); + for (ProtocolMapperRepresentation model : clientRepresentation.getProtocolMappers()) { + if (model.getName().equals("address") + || model.getName().equals("hard") + || model.getName().equals("hard-nested") + || model.getName().equals("custom phone") + || model.getName().equals("departments") + || model.getName().equals("nested phone") + || model.getName().equals("rename-app-role") + || model.getName().equals("hard-realm") + || model.getName().equals("hard-app") + ) { + app.getProtocolMappers().delete(model.getId()); + } + } + } + + events.clear(); + + + { + OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password"); + IDToken idToken = oauth.verifyIDToken(response.getIdToken()); + assertNull(idToken.getAddress()); + assertNull(idToken.getOtherClaims().get("home_phone")); + assertNull(idToken.getOtherClaims().get("hard")); + assertNull(idToken.getOtherClaims().get("nested")); + assertNull(idToken.getOtherClaims().get("department")); + } + + + events.clear(); + } + + + @Test + public void testUserRoleToAttributeMappers() throws Exception { + // Add mapper for realm roles + ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true); + ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper("test-app", null, "Client roles mapper", "roles-custom.test-app", true, true); + + ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers(); + protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper)); + + // Login user + OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password"); + IDToken idToken = oauth.verifyIDToken(response.getIdToken()); + + // Verify attribute is filled + Map roleMappings = (Map)idToken.getOtherClaims().get("roles-custom"); + Assert.assertEquals(2, roleMappings.size()); + String realmRoleMappings = (String) roleMappings.get("realm"); + String testAppMappings = (String) roleMappings.get("test-app"); + Assert.assertTrue(realmRoleMappings.contains("pref.user")); + Assert.assertEquals("[customer-user]", testAppMappings); + } + + + private ProtocolMapperRepresentation makeMapper(String name, String mapperType, Map config) { + ProtocolMapperRepresentation rep = new ProtocolMapperRepresentation(); + rep.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + rep.setName(name); + rep.setProtocolMapper(mapperType); + rep.setConfig(config); + rep.setConsentRequired(true); + rep.setConsentText("Test Consent Text"); + return rep; + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/GroupBuilder.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/GroupBuilder.java new file mode 100644 index 0000000000..0968ead475 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/GroupBuilder.java @@ -0,0 +1,85 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed 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. + */ + +package org.keycloak.testsuite.util; + +import java.util.List; +import java.util.Map; +import org.keycloak.representations.idm.GroupRepresentation; + +/** + * + * @author Hynek Mlnarik + */ +public class GroupBuilder { + + private final GroupRepresentation rep; + + public static GroupBuilder create() { + final GroupRepresentation rep = new GroupRepresentation(); + return new GroupBuilder(rep); + } + + private GroupBuilder(GroupRepresentation rep) { + this.rep = rep; + } + + public GroupRepresentation build() { + return rep; + } + + public GroupBuilder id(String id) { + rep.setId(id); + return this; + } + + public GroupBuilder name(String name) { + rep.setName(name); + return this; + } + + public GroupBuilder path(String path) { + rep.setPath(path); + return this; + } + + public GroupBuilder realmRoles(List realmRoles) { + rep.setRealmRoles(realmRoles); + return this; + } + + public GroupBuilder clientRoles(Map> clientRoles) { + rep.setClientRoles(clientRoles); + return this; + } + + public GroupBuilder attributes(Map> attributes) { + rep.setAttributes(attributes); + return this; + } + + public GroupBuilder singleAttribute(String name, String value) { + rep.singleAttribute(name, value); + return this; + } + + public GroupBuilder subGroups(List subGroups) { + rep.setSubGroups(subGroups); + return this; + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java index 62018e1c0e..6bcea06cd2 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java @@ -6,6 +6,8 @@ import org.keycloak.protocol.oidc.mappers.HardcodedClaim; import org.keycloak.protocol.oidc.mappers.HardcodedRole; import org.keycloak.protocol.oidc.mappers.RoleNameMapper; import org.keycloak.protocol.oidc.mappers.UserAttributeMapper; +import org.keycloak.protocol.oidc.mappers.UserClientRoleMappingMapper; +import org.keycloak.protocol.oidc.mappers.UserRealmRoleMappingMapper; import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper; import org.keycloak.representations.idm.ProtocolMapperRepresentation; @@ -103,4 +105,22 @@ public class ProtocolMapperUtil { consentRequired, consentText, accessToken, idToken)); } + + + public static ProtocolMapperRepresentation createUserRealmRoleMappingMapper(String realmRolePrefix, + String name, + String tokenClaimName, + boolean accessToken, boolean idToken) { + + return ModelToRepresentation.toRepresentation(UserRealmRoleMappingMapper.create(realmRolePrefix, name, tokenClaimName, accessToken, idToken)); + } + + + public static ProtocolMapperRepresentation createUserClientRoleMappingMapper(String clientId, String clientRolePrefix, + String name, + String tokenClaimName, + boolean accessToken, boolean idToken) { + + return ModelToRepresentation.toRepresentation(UserClientRoleMappingMapper.create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken)); + } } diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/WildflyOIDCFilterAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/WildflyOIDCFilterAdapterTest.java new file mode 100644 index 0000000000..3427f33a31 --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/WildflyOIDCFilterAdapterTest.java @@ -0,0 +1,12 @@ +package org.keycloak.testsuite.adapter; + +import org.keycloak.testsuite.adapter.servlet.AbstractDemoFilterServletAdapterTest; +import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; + +/** + * Created by zschwarz on 9/14/16. + */ + +@AppServerContainer("app-server-wildfly") +public class WildflyOIDCFilterAdapterTest extends AbstractDemoFilterServletAdapterTest{ +} diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml index 1683bc9b65..d30721ca6e 100755 --- a/testsuite/integration-arquillian/tests/pom.xml +++ b/testsuite/integration-arquillian/tests/pom.xml @@ -405,16 +405,10 @@ - + - migration-auto - - - migration.mode - auto - - + auth-server-migration -Dkeycloak.migration.action=import @@ -438,6 +432,9 @@ migrated.auth.server.version + + migration.mode + diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java index 9cad056222..ed1e757228 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java @@ -242,7 +242,7 @@ public abstract class AbstractKeycloakIdentityProviderTest extends AbstractIdent * Test for KEYCLOAK-1053 - verify email action is not performed if email is not provided, login is normal, but action stays in set to be performed later */ @Test - public void testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled() { + public void testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled() throws Exception { RealmModel realm = getRealm(); realm.setVerifyEmail(true); setUpdateProfileFirstLogin(realm, IdentityProviderRepresentation.UPFLM_OFF); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java index 0552875a4e..58c4ca18f3 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java @@ -151,7 +151,7 @@ public class OIDCKeyCloakServerBrokerBasicTest extends AbstractKeycloakIdentityP } @Test - public void testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled() { + public void testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled() throws Exception { super.testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled(); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java index 9b7f0d2462..9fe49f21da 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java @@ -105,12 +105,16 @@ public class UserConsentModelTest extends AbstractModelTest { Assert.assertTrue(isRoleGranted(realm, "realm-role", johnFooConsent)); Assert.assertTrue(isRoleGranted(barClient, "bar-client-role", johnFooConsent)); Assert.assertTrue(isMapperGranted(fooClient, "foo", johnFooConsent)); + Assert.assertNotNull("Created Date should be set", johnFooConsent.getCreatedDate()); + Assert.assertNotNull("Last Updated Date should be set", johnFooConsent.getLastUpdatedDate()); UserConsentModel johnBarConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), barClient.getId()); Assert.assertEquals(johnBarConsent.getGrantedRoles().size(), 1); Assert.assertEquals(johnBarConsent.getGrantedProtocolMappers().size(), 1); Assert.assertTrue(isRoleGranted(realm, "realm-role", johnBarConsent)); Assert.assertTrue(isMapperGranted(barClient, "bar", johnBarConsent)); + Assert.assertNotNull("Created Date should be set", johnBarConsent.getCreatedDate()); + Assert.assertNotNull("Last Updated Date should be set", johnBarConsent.getLastUpdatedDate()); UserConsentModel maryConsent = realmManager.getSession().users().getConsentByClient(realm, mary.getId(), fooClient.getId()); Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1); @@ -118,6 +122,8 @@ public class UserConsentModelTest extends AbstractModelTest { Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent)); Assert.assertFalse(isRoleGranted(barClient, "bar-client-role", maryConsent)); Assert.assertTrue(isMapperGranted(fooClient, "foo", maryConsent)); + Assert.assertNotNull("Created Date should be set", maryConsent.getCreatedDate()); + Assert.assertNotNull("Last Updated Date should be set", maryConsent.getLastUpdatedDate()); Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, mary.getId(), barClient.getId())); } @@ -176,6 +182,7 @@ public class UserConsentModelTest extends AbstractModelTest { Assert.assertFalse(isRoleGranted(realm, "realm-role", johnConsent)); Assert.assertTrue(isRoleGranted(realm, "new-realm-role", johnConsent)); Assert.assertFalse(isMapperGranted(fooClient, "foo", johnConsent)); + Assert.assertTrue("Created date should be less than last updated date", johnConsent.getCreatedDate() < johnConsent.getLastUpdatedDate()); } @Test diff --git a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json index 767ab44a3b..05cb0e9340 100755 --- a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json +++ b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json @@ -26,8 +26,8 @@ "claim.name": "mobile", "Claim JSON Type": "String", "access.token.claim": "true", - "id.token.claim": "true" - + "id.token.claim": "true", + "userinfo.token.claim": "true" } }, { @@ -40,8 +40,8 @@ "claim.name": "preferred_username", "Claim JSON Type": "String", "access.token.claim": "true", - "id.token.claim": "true" - + "id.token.claim": "true", + "userinfo.token.claim": "true" } }, { @@ -54,8 +54,8 @@ "claim.name": "email", "Claim JSON Type": "String", "access.token.claim": "true", - "id.token.claim": "true" - + "id.token.claim": "true", + "userinfo.token.claim": "true" } }, { @@ -68,8 +68,8 @@ "claim.name": "given_name", "Claim JSON Type": "String", "access.token.claim": "true", - "id.token.claim": "true" - + "id.token.claim": "true", + "userinfo.token.claim": "true" } }, { @@ -83,7 +83,6 @@ "Claim JSON Type": "String", "access.token.claim": "true", "id.token.claim": "true" - } }, { @@ -93,8 +92,8 @@ "consentRequired": false, "config": { "access.token.claim": "true", - "id.token.claim": "true" - + "id.token.claim": "true", + "userinfo.token.claim": "true" } } diff --git a/themes/src/main/resources/theme/base/admin/index.ftl b/themes/src/main/resources/theme/base/admin/index.ftl index 76286b17b8..7ab0bc947e 100755 --- a/themes/src/main/resources/theme/base/admin/index.ftl +++ b/themes/src/main/resources/theme/base/admin/index.ftl @@ -22,7 +22,7 @@ - + diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index ef27439102..ef8ef749c5 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -896,6 +896,8 @@ spi=SPI granted-roles=Granted Roles granted-protocol-mappers=Granted Protocol Mappers additional-grants=Additional Grants +consent-created-date=Created +consent-last-updated-date=Last updated revoke=Revoke new-password=New Password password-confirmation=Password Confirmation diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_no.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_no.properties index 574eb9156a..9b8a6cb28f 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_no.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_no.properties @@ -863,6 +863,8 @@ spi=SPI granted-roles=Tildelte roller granted-protocol-mappers=Innvilgede protokollmappere additional-grants=Tillegsrettigheter +consent-created-date=Opprettet +consent-last-updated-date=Sist oppdatert revoke=Opphev new-password=Nytt passord password-confirmation=Passord bekreftelse diff --git a/themes/src/main/resources/theme/base/admin/resources/js/app.js b/themes/src/main/resources/theme/base/admin/resources/js/app.js index f75e66dab3..e892b4544b 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/app.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/app.js @@ -953,6 +953,9 @@ module.config([ '$routeProvider', function($routeProvider) { }, mapper : function(ClientProtocolMapperLoader) { return ClientProtocolMapperLoader(); + }, + clients : function(ClientListLoader) { + return ClientListLoader(); } }, @@ -969,6 +972,9 @@ module.config([ '$routeProvider', function($routeProvider) { }, client : function(ClientLoader) { return ClientLoader(); + }, + clients : function(ClientListLoader) { + return ClientListLoader(); } }, controller : 'ClientProtocolMapperCreateCtrl' @@ -1017,6 +1023,9 @@ module.config([ '$routeProvider', function($routeProvider) { }, mapper : function(ClientTemplateProtocolMapperLoader) { return ClientTemplateProtocolMapperLoader(); + }, + clients : function(ClientListLoader) { + return ClientListLoader(); } }, @@ -1033,6 +1042,9 @@ module.config([ '$routeProvider', function($routeProvider) { }, template : function(ClientTemplateLoader) { return ClientTemplateLoader(); + }, + clients : function(ClientListLoader) { + return ClientListLoader(); } }, controller : 'ClientTemplateProtocolMapperCreateCtrl' @@ -2802,3 +2814,81 @@ module.directive('kcOnReadFile', function ($parse) { } }; }); + +module.controller('PagingCtrl', function ($scope) { + $scope.currentPageInput = 1; + + $scope.firstPage = function() { + if (!$scope.hasPrevious()) return; + $scope.currentPage = 1; + $scope.currentPageInput = 1; + }; + + $scope.lastPage = function() { + if (!$scope.hasNext()) return; + $scope.currentPage = $scope.numberOfPages; + $scope.currentPageInput = $scope.numberOfPages; + }; + + $scope.previousPage = function() { + if (!$scope.hasPrevious()) return; + $scope.currentPage--; + $scope.currentPageInput = $scope.currentPage; + }; + + $scope.nextPage = function() { + if (!$scope.hasNext()) return; + $scope.currentPage++; + $scope.currentPageInput = $scope.currentPage; + }; + + $scope.hasNext = function() { + return $scope.currentPage < $scope.numberOfPages; + }; + + $scope.hasPrevious = function() { + return $scope.currentPage > 1; + }; +}); + +module.directive('kcPaging', function () { + return { + scope: { + currentPage: '=', + currentPageInput: '=', + numberOfPages: '=' + }, + restrict: 'E', + replace: true, + controller: 'PagingCtrl', + templateUrl: resourceUrl + '/templates/kc-paging.html' + } +}); + +// Tests the page number input from currentPageInput to see +// if it represents a valid page. If so, the current page is changed. +module.directive('kcValidPage', function() { + return { + require: 'ngModel', + link: function(scope, element, attrs, ctrl) { + ctrl.$validators.inRange = function(modelValue, viewValue) { + if (viewValue >= 1 && viewValue <= scope.numberOfPages) { + scope.currentPage = viewValue; + } + + return true; + } + } + } +}); + +// filter used for paged tables +module.filter('startFrom', function () { + return function (input, start) { + if (input) { + start = +start; + return input.slice(start); + } + return []; + }; +}); \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js index 4b33b14cb6..3405791c03 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js @@ -731,9 +731,21 @@ module.controller('ClientImportCtrl', function($scope, $location, $upload, realm }); -module.controller('ClientListCtrl', function($scope, realm, clients, Client, serverInfo, $route, Dialog, Notifications) { +module.controller('ClientListCtrl', function($scope, realm, clients, Client, serverInfo, $route, Dialog, Notifications, filterFilter) { $scope.realm = realm; $scope.clients = clients; + $scope.currentPage = 1; + $scope.currentPageInput = 1; + $scope.pageSize = 20; + $scope.numberOfPages = Math.ceil($scope.clients.length/$scope.pageSize); + + $scope.$watch('search', function (newVal, oldVal) { + $scope.filtered = filterFilter($scope.clients, newVal); + $scope.totalItems = $scope.filtered.length; + $scope.numberOfPages = Math.ceil($scope.totalItems/$scope.pageSize); + $scope.currentPage = 1; + $scope.currentPageInput = 1; + }, true); $scope.removeClient = function(client) { Dialog.confirmDelete(client.clientId, 'client', function() { @@ -1693,8 +1705,10 @@ module.controller('ClientProtocolMapperListCtrl', function($scope, realm, client updateMappers(); }); -module.controller('ClientProtocolMapperCtrl', function($scope, realm, serverInfo, client, mapper, ClientProtocolMapper, Notifications, Dialog, $location) { +module.controller('ClientProtocolMapperCtrl', function($scope, realm, serverInfo, client, clients, mapper, ClientProtocolMapper, Notifications, Dialog, $location) { $scope.realm = realm; + $scope.clients = clients; + /* $scope.client = client; $scope.create = false; @@ -1774,8 +1788,9 @@ module.controller('ClientProtocolMapperCtrl', function($scope, realm, serverInfo }); -module.controller('ClientProtocolMapperCreateCtrl', function($scope, realm, serverInfo, client, ClientProtocolMapper, Notifications, Dialog, $location) { +module.controller('ClientProtocolMapperCreateCtrl', function($scope, realm, serverInfo, client, clients, ClientProtocolMapper, Notifications, Dialog, $location) { $scope.realm = realm; + $scope.clients = clients; if (client.protocol == null) { client.protocol = 'openid-connect'; @@ -1818,6 +1833,8 @@ module.controller('ClientProtocolMapperCreateCtrl', function($scope, realm, serv }, function(error) { if (error.status == 400 && error.data.error_description) { Notifications.error(error.data.error_description); + } else if (error.status == 409 && error.data.errorMessage) { + Notifications.error(error.data.errorMessage); } else { Notifications.error('Unexpected error when updating protocol mapper'); } @@ -1987,8 +2004,10 @@ module.controller('ClientTemplateProtocolMapperListCtrl', function($scope, realm updateMappers(); }); -module.controller('ClientTemplateProtocolMapperCtrl', function($scope, realm, serverInfo, template, mapper, ClientTemplateProtocolMapper, Notifications, Dialog, $location) { +module.controller('ClientTemplateProtocolMapperCtrl', function($scope, realm, serverInfo, template, mapper, clients, ClientTemplateProtocolMapper, Notifications, Dialog, $location) { $scope.realm = realm; + $scope.clients = clients; + if (template.protocol == null) { template.protocol = 'openid-connect'; } @@ -2054,8 +2073,10 @@ module.controller('ClientTemplateProtocolMapperCtrl', function($scope, realm, se }); -module.controller('ClientTemplateProtocolMapperCreateCtrl', function($scope, realm, serverInfo, template, ClientTemplateProtocolMapper, Notifications, Dialog, $location) { +module.controller('ClientTemplateProtocolMapperCreateCtrl', function($scope, realm, serverInfo, template, clients, ClientTemplateProtocolMapper, Notifications, Dialog, $location) { $scope.realm = realm; + $scope.clients = clients; + if (template.protocol == null) { template.protocol = 'openid-connect'; } diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js index 568ebdf591..a7135f6e0e 100644 --- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js @@ -458,6 +458,7 @@ module.controller('RealmPasswordPolicyCtrl', function($scope, Realm, realm, $htt var value; if (policyToken.indexOf('(') == -1) { id = policyToken.trim(); + value = null; } else { id = policyToken.substring(0, policyToken.indexOf('(')); value = policyToken.substring(policyToken.indexOf('(') + 1, policyToken.indexOf(')')).trim(); @@ -492,7 +493,14 @@ module.controller('RealmPasswordPolicyCtrl', function($scope, Realm, realm, $htt $scope.realm = realm; $scope.serverInfo = serverInfo; - $scope.changed = false; $scope.policy = parse(realm.passwordPolicy); + + $scope.changed = false; + $scope.policy = parse(realm.passwordPolicy); + var oldCopy = angular.copy($scope.policy); + + $scope.$watch('policy', function() { + $scope.changed = ! angular.equals($scope.policy, oldCopy); + }, true); $scope.addPolicy = function(policy){ policy.value = policy.defaultValue; @@ -500,21 +508,18 @@ module.controller('RealmPasswordPolicyCtrl', function($scope, Realm, realm, $htt $scope.policy = []; } $scope.policy.push(policy); - $scope.changed = true; } $scope.removePolicy = function(index){ $scope.policy.splice(index, 1); - $scope.changed = true; } $scope.save = function() { - $scope.changed = false; $scope.realm.passwordPolicy = toString($scope.policy); console.debug($scope.realm.passwordPolicy); Realm.update($scope.realm, function () { - $location.url("/realms/" + realm.realm + "/authentication/password-policy"); + $route.reload(); Notifications.success("Your changes have been saved to the realm."); }); }; @@ -1291,9 +1296,21 @@ module.controller('RealmRevocationCtrl', function($scope, Realm, RealmPushRevoca }); -module.controller('RoleListCtrl', function($scope, $route, Dialog, Notifications, realm, roles, RoleById) { +module.controller('RoleListCtrl', function($scope, $route, Dialog, Notifications, realm, roles, RoleById, filterFilter) { $scope.realm = realm; $scope.roles = roles; + $scope.currentPage = 1; + $scope.currentPageInput = 1; + $scope.pageSize = 20; + $scope.numberOfPages = Math.ceil($scope.roles.length/$scope.pageSize); + + $scope.$watch('searchQuery', function (newVal, oldVal) { + $scope.filtered = filterFilter($scope.roles, {name: newVal}); + $scope.totalItems = $scope.filtered.length; + $scope.numberOfPages = Math.ceil($scope.totalItems/$scope.pageSize); + $scope.currentPage = 1; + $scope.currentPageInput = 1; + }, true); $scope.removeRole = function (role) { Dialog.confirmDelete(role.name, 'role', function () { @@ -1874,14 +1891,12 @@ module.controller('CreateFlowCtrl', function($scope, realm, }; }); -module.controller('CreateExecutionFlowCtrl', function($scope, realm, topFlow, parentFlow, formProviders, +module.controller('CreateExecutionFlowCtrl', function($scope, realm, parentFlow, formProviders, CreateExecutionFlow, Notifications, $location) { $scope.realm = realm; $scope.formProviders = formProviders; - var returnToTopFlow = parentFlow.topLevel ? parentFlow.alias : topFlow; - var defaultFlowType = parentFlow.providerId == 'client-flow' ? 'client-flow' : 'basic-flow'; $scope.flow = { alias: "", @@ -1896,23 +1911,21 @@ module.controller('CreateExecutionFlowCtrl', function($scope, realm, topFlow, pa $scope.save = function() { $scope.flow.provider = $scope.provider.id; CreateExecutionFlow.save({realm: realm.realm, alias: parentFlow.alias}, $scope.flow, function() { - $location.url("/realms/" + realm.realm + "/authentication/flows/" + returnToTopFlow); + $location.url("/realms/" + realm.realm + "/authentication/flows"); Notifications.success("Flow Created."); }) } $scope.cancel = function() { - $location.url("/realms/" + realm.realm + "/authentication/flows/" + returnToTopFlow); + $location.url("/realms/" + realm.realm + "/authentication/flows"); }; }); -module.controller('CreateExecutionCtrl', function($scope, realm, topFlow, parentFlow, formActionProviders, authenticatorProviders, clientAuthenticatorProviders, +module.controller('CreateExecutionCtrl', function($scope, realm, parentFlow, formActionProviders, authenticatorProviders, clientAuthenticatorProviders, CreateExecution, Notifications, $location) { $scope.realm = realm; $scope.parentFlow = parentFlow; - var returnToTopFlow = parentFlow.topLevel ? parentFlow.alias : topFlow; - if (parentFlow.providerId == 'form-flow') { $scope.providers = formActionProviders; } else if (parentFlow.providerId == 'client-flow') { @@ -1931,23 +1944,32 @@ module.controller('CreateExecutionCtrl', function($scope, realm, topFlow, parent provider: $scope.provider.id } CreateExecution.save({realm: realm.realm, alias: parentFlow.alias}, execution, function() { - $location.url("/realms/" + realm.realm + "/authentication/flows/" + returnToTopFlow); + $location.url("/realms/" + realm.realm + "/authentication/flows"); Notifications.success("Execution Created."); }) } $scope.cancel = function() { - $location.url("/realms/" + realm.realm + "/authentication/flows/" + returnToTopFlow); + $location.url("/realms/" + realm.realm + "/authentication/flows"); }; }); -module.controller('AuthenticationFlowsCtrl', function($scope, $route, realm, flows, selectedFlow, +module.controller('AuthenticationFlowsCtrl', function($scope, $route, realm, flows, selectedFlow, LastFlowSelected, AuthenticationFlows, AuthenticationFlowsCopy, AuthenticationFlowExecutions, AuthenticationExecution, AuthenticationExecutionRaisePriority, AuthenticationExecutionLowerPriority, $modal, Notifications, CopyDialog, $location) { $scope.realm = realm; $scope.flows = flows; + + if (selectedFlow !== null) { + LastFlowSelected.alias = selectedFlow; + } + + if (selectedFlow === null && LastFlowSelected.alias !== null) { + selectedFlow = LastFlowSelected.alias; + } + if (flows.length > 0) { $scope.flow = flows[0]; if (selectedFlow) { diff --git a/themes/src/main/resources/theme/base/admin/resources/js/services.js b/themes/src/main/resources/theme/base/admin/resources/js/services.js index 73f298ee00..66ea756d8e 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/services.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/services.js @@ -347,6 +347,11 @@ module.service('UserSearchState', function() { }; }); +// Service tracks the last flow selected in Authentication-->Flows tab +module.service('LastFlowSelected', function() { + this.alias = null; +}); + module.factory('UserFederationInstances', function($resource) { return $resource(authUrl + '/admin/realms/:realm/user-federation/instances/:instance', { realm : '@realm', diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html index 7f796e9960..51aaa2009b 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html @@ -4,52 +4,53 @@ {{:: 'clients.tooltip' | translate}} - +
    - - + - - - - - - - + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/menu.html b/themes/src/main/resources/theme/base/admin/resources/partials/menu.html index eb44793949..bc066b0ec7 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/menu.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/menu.html @@ -5,7 +5,7 @@ - + {{model.mapperType.helpText}} - +
    diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/role-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/role-list.html index 8b2bd26566..5c0d47304e 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/role-list.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/role-list.html @@ -6,47 +6,48 @@
  • {{:: 'default-roles' | translate}}
  • - +
    - - + - - - - - - - + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html index 63bfa7e528..670d7081f7 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html @@ -13,6 +13,8 @@ {{:: 'granted-roles' | translate}} {{:: 'granted-protocol-mappers' | translate}} {{:: 'additional-grants' | translate}} + {{:: 'consent-created-date' | translate}} + {{:: 'consent-last-updated-date' | translate}} {{:: 'action' | translate}} @@ -41,6 +43,8 @@ , {{additionalGrant.key}} + {{consent.createdDate | date :'short'}} + {{consent.lastUpdatedDate | date :'short'}} {{:: 'revoke' | translate}} diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-component-config.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-component-config.html index dccb17e515..b3b536f1d8 100755 --- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-component-config.html +++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-component-config.html @@ -12,13 +12,13 @@
    -
    diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-paging.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-paging.html new file mode 100644 index 0000000000..653e4a58c9 --- /dev/null +++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-paging.html @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-provider-config.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-provider-config.html index 2b5f00e5f3..0309bb3227 100755 --- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-provider-config.html +++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-provider-config.html @@ -12,7 +12,7 @@
    -
    diff --git a/themes/src/main/resources/theme/keycloak/common/resources/lib/angular/angular-mocks.js b/themes/src/main/resources/theme/keycloak/common/resources/lib/angular/angular-mocks.js deleted file mode 100644 index 8b7c1f29b8..0000000000 --- a/themes/src/main/resources/theme/keycloak/common/resources/lib/angular/angular-mocks.js +++ /dev/null @@ -1,2433 +0,0 @@ -/** - * @license AngularJS v1.4.4 - * (c) 2010-2015 Google, Inc. http://angularjs.org - * License: MIT - */ -(function(window, angular, undefined) { - -'use strict'; - -/** - * @ngdoc object - * @name angular.mock - * @description - * - * Namespace from 'angular-mocks.js' which contains testing related code. - */ -angular.mock = {}; - -/** - * ! This is a private undocumented service ! - * - * @name $browser - * - * @description - * This service is a mock implementation of {@link ng.$browser}. It provides fake - * implementation for commonly used browser apis that are hard to test, e.g. setTimeout, xhr, - * cookies, etc... - * - * The api of this service is the same as that of the real {@link ng.$browser $browser}, except - * that there are several helper methods available which can be used in tests. - */ -angular.mock.$BrowserProvider = function() { - this.$get = function() { - return new angular.mock.$Browser(); - }; -}; - -angular.mock.$Browser = function() { - var self = this; - - this.isMock = true; - self.$$url = "http://server/"; - self.$$lastUrl = self.$$url; // used by url polling fn - self.pollFns = []; - - // TODO(vojta): remove this temporary api - self.$$completeOutstandingRequest = angular.noop; - self.$$incOutstandingRequestCount = angular.noop; - - - // register url polling fn - - self.onUrlChange = function(listener) { - self.pollFns.push( - function() { - if (self.$$lastUrl !== self.$$url || self.$$state !== self.$$lastState) { - self.$$lastUrl = self.$$url; - self.$$lastState = self.$$state; - listener(self.$$url, self.$$state); - } - } - ); - - return listener; - }; - - self.$$applicationDestroyed = angular.noop; - self.$$checkUrlChange = angular.noop; - - self.deferredFns = []; - self.deferredNextId = 0; - - self.defer = function(fn, delay) { - delay = delay || 0; - self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId}); - self.deferredFns.sort(function(a, b) { return a.time - b.time;}); - return self.deferredNextId++; - }; - - - /** - * @name $browser#defer.now - * - * @description - * Current milliseconds mock time. - */ - self.defer.now = 0; - - - self.defer.cancel = function(deferId) { - var fnIndex; - - angular.forEach(self.deferredFns, function(fn, index) { - if (fn.id === deferId) fnIndex = index; - }); - - if (fnIndex !== undefined) { - self.deferredFns.splice(fnIndex, 1); - return true; - } - - return false; - }; - - - /** - * @name $browser#defer.flush - * - * @description - * Flushes all pending requests and executes the defer callbacks. - * - * @param {number=} number of milliseconds to flush. See {@link #defer.now} - */ - self.defer.flush = function(delay) { - if (angular.isDefined(delay)) { - self.defer.now += delay; - } else { - if (self.deferredFns.length) { - self.defer.now = self.deferredFns[self.deferredFns.length - 1].time; - } else { - throw new Error('No deferred tasks to be flushed'); - } - } - - while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) { - self.deferredFns.shift().fn(); - } - }; - - self.$$baseHref = '/'; - self.baseHref = function() { - return this.$$baseHref; - }; -}; -angular.mock.$Browser.prototype = { - -/** - * @name $browser#poll - * - * @description - * run all fns in pollFns - */ - poll: function poll() { - angular.forEach(this.pollFns, function(pollFn) { - pollFn(); - }); - }, - - url: function(url, replace, state) { - if (angular.isUndefined(state)) { - state = null; - } - if (url) { - this.$$url = url; - // Native pushState serializes & copies the object; simulate it. - this.$$state = angular.copy(state); - return this; - } - - return this.$$url; - }, - - state: function() { - return this.$$state; - }, - - notifyWhenNoOutstandingRequests: function(fn) { - fn(); - } -}; - - -/** - * @ngdoc provider - * @name $exceptionHandlerProvider - * - * @description - * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors - * passed to the `$exceptionHandler`. - */ - -/** - * @ngdoc service - * @name $exceptionHandler - * - * @description - * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed - * to it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration - * information. - * - * - * ```js - * describe('$exceptionHandlerProvider', function() { - * - * it('should capture log messages and exceptions', function() { - * - * module(function($exceptionHandlerProvider) { - * $exceptionHandlerProvider.mode('log'); - * }); - * - * inject(function($log, $exceptionHandler, $timeout) { - * $timeout(function() { $log.log(1); }); - * $timeout(function() { $log.log(2); throw 'banana peel'; }); - * $timeout(function() { $log.log(3); }); - * expect($exceptionHandler.errors).toEqual([]); - * expect($log.assertEmpty()); - * $timeout.flush(); - * expect($exceptionHandler.errors).toEqual(['banana peel']); - * expect($log.log.logs).toEqual([[1], [2], [3]]); - * }); - * }); - * }); - * ``` - */ - -angular.mock.$ExceptionHandlerProvider = function() { - var handler; - - /** - * @ngdoc method - * @name $exceptionHandlerProvider#mode - * - * @description - * Sets the logging mode. - * - * @param {string} mode Mode of operation, defaults to `rethrow`. - * - * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log` - * mode stores an array of errors in `$exceptionHandler.errors`, to allow later - * assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and - * {@link ngMock.$log#reset reset()} - * - `rethrow`: If any errors are passed to the handler in tests, it typically means that there - * is a bug in the application or test, so this mock will make these tests fail. - * For any implementations that expect exceptions to be thrown, the `rethrow` mode - * will also maintain a log of thrown errors. - */ - this.mode = function(mode) { - - switch (mode) { - case 'log': - case 'rethrow': - var errors = []; - handler = function(e) { - if (arguments.length == 1) { - errors.push(e); - } else { - errors.push([].slice.call(arguments, 0)); - } - if (mode === "rethrow") { - throw e; - } - }; - handler.errors = errors; - break; - default: - throw new Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!"); - } - }; - - this.$get = function() { - return handler; - }; - - this.mode('rethrow'); -}; - - -/** - * @ngdoc service - * @name $log - * - * @description - * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays - * (one array per logging level). These arrays are exposed as `logs` property of each of the - * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`. - * - */ -angular.mock.$LogProvider = function() { - var debug = true; - - function concat(array1, array2, index) { - return array1.concat(Array.prototype.slice.call(array2, index)); - } - - this.debugEnabled = function(flag) { - if (angular.isDefined(flag)) { - debug = flag; - return this; - } else { - return debug; - } - }; - - this.$get = function() { - var $log = { - log: function() { $log.log.logs.push(concat([], arguments, 0)); }, - warn: function() { $log.warn.logs.push(concat([], arguments, 0)); }, - info: function() { $log.info.logs.push(concat([], arguments, 0)); }, - error: function() { $log.error.logs.push(concat([], arguments, 0)); }, - debug: function() { - if (debug) { - $log.debug.logs.push(concat([], arguments, 0)); - } - } - }; - - /** - * @ngdoc method - * @name $log#reset - * - * @description - * Reset all of the logging arrays to empty. - */ - $log.reset = function() { - /** - * @ngdoc property - * @name $log#log.logs - * - * @description - * Array of messages logged using {@link ng.$log#log `log()`}. - * - * @example - * ```js - * $log.log('Some Log'); - * var first = $log.log.logs.unshift(); - * ``` - */ - $log.log.logs = []; - /** - * @ngdoc property - * @name $log#info.logs - * - * @description - * Array of messages logged using {@link ng.$log#info `info()`}. - * - * @example - * ```js - * $log.info('Some Info'); - * var first = $log.info.logs.unshift(); - * ``` - */ - $log.info.logs = []; - /** - * @ngdoc property - * @name $log#warn.logs - * - * @description - * Array of messages logged using {@link ng.$log#warn `warn()`}. - * - * @example - * ```js - * $log.warn('Some Warning'); - * var first = $log.warn.logs.unshift(); - * ``` - */ - $log.warn.logs = []; - /** - * @ngdoc property - * @name $log#error.logs - * - * @description - * Array of messages logged using {@link ng.$log#error `error()`}. - * - * @example - * ```js - * $log.error('Some Error'); - * var first = $log.error.logs.unshift(); - * ``` - */ - $log.error.logs = []; - /** - * @ngdoc property - * @name $log#debug.logs - * - * @description - * Array of messages logged using {@link ng.$log#debug `debug()`}. - * - * @example - * ```js - * $log.debug('Some Error'); - * var first = $log.debug.logs.unshift(); - * ``` - */ - $log.debug.logs = []; - }; - - /** - * @ngdoc method - * @name $log#assertEmpty - * - * @description - * Assert that all of the logging methods have no logged messages. If any messages are present, - * an exception is thrown. - */ - $log.assertEmpty = function() { - var errors = []; - angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) { - angular.forEach($log[logLevel].logs, function(log) { - angular.forEach(log, function(logItem) { - errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' + - (logItem.stack || '')); - }); - }); - }); - if (errors.length) { - errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or " + - "an expected log message was not checked and removed:"); - errors.push(''); - throw new Error(errors.join('\n---------\n')); - } - }; - - $log.reset(); - return $log; - }; -}; - - -/** - * @ngdoc service - * @name $interval - * - * @description - * Mock implementation of the $interval service. - * - * Use {@link ngMock.$interval#flush `$interval.flush(millis)`} to - * move forward by `millis` milliseconds and trigger any functions scheduled to run in that - * time. - * - * @param {function()} fn A function that should be called repeatedly. - * @param {number} delay Number of milliseconds between each function call. - * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat - * indefinitely. - * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise - * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. - * @param {...*=} Pass additional parameters to the executed function. - * @returns {promise} A promise which will be notified on each iteration. - */ -angular.mock.$IntervalProvider = function() { - this.$get = ['$browser', '$rootScope', '$q', '$$q', - function($browser, $rootScope, $q, $$q) { - var repeatFns = [], - nextRepeatId = 0, - now = 0; - - var $interval = function(fn, delay, count, invokeApply) { - var hasParams = arguments.length > 4, - args = hasParams ? Array.prototype.slice.call(arguments, 4) : [], - iteration = 0, - skipApply = (angular.isDefined(invokeApply) && !invokeApply), - deferred = (skipApply ? $$q : $q).defer(), - promise = deferred.promise; - - count = (angular.isDefined(count)) ? count : 0; - promise.then(null, null, (!hasParams) ? fn : function() { - fn.apply(null, args); - }); - - promise.$$intervalId = nextRepeatId; - - function tick() { - deferred.notify(iteration++); - - if (count > 0 && iteration >= count) { - var fnIndex; - deferred.resolve(iteration); - - angular.forEach(repeatFns, function(fn, index) { - if (fn.id === promise.$$intervalId) fnIndex = index; - }); - - if (fnIndex !== undefined) { - repeatFns.splice(fnIndex, 1); - } - } - - if (skipApply) { - $browser.defer.flush(); - } else { - $rootScope.$apply(); - } - } - - repeatFns.push({ - nextTime:(now + delay), - delay: delay, - fn: tick, - id: nextRepeatId, - deferred: deferred - }); - repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;}); - - nextRepeatId++; - return promise; - }; - /** - * @ngdoc method - * @name $interval#cancel - * - * @description - * Cancels a task associated with the `promise`. - * - * @param {promise} promise A promise from calling the `$interval` function. - * @returns {boolean} Returns `true` if the task was successfully cancelled. - */ - $interval.cancel = function(promise) { - if (!promise) return false; - var fnIndex; - - angular.forEach(repeatFns, function(fn, index) { - if (fn.id === promise.$$intervalId) fnIndex = index; - }); - - if (fnIndex !== undefined) { - repeatFns[fnIndex].deferred.reject('canceled'); - repeatFns.splice(fnIndex, 1); - return true; - } - - return false; - }; - - /** - * @ngdoc method - * @name $interval#flush - * @description - * - * Runs interval tasks scheduled to be run in the next `millis` milliseconds. - * - * @param {number=} millis maximum timeout amount to flush up until. - * - * @return {number} The amount of time moved forward. - */ - $interval.flush = function(millis) { - now += millis; - while (repeatFns.length && repeatFns[0].nextTime <= now) { - var task = repeatFns[0]; - task.fn(); - task.nextTime += task.delay; - repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;}); - } - return millis; - }; - - return $interval; - }]; -}; - - -/* jshint -W101 */ -/* The R_ISO8061_STR regex is never going to fit into the 100 char limit! - * This directive should go inside the anonymous function but a bug in JSHint means that it would - * not be enacted early enough to prevent the warning. - */ -var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/; - -function jsonStringToDate(string) { - var match; - if (match = string.match(R_ISO8061_STR)) { - var date = new Date(0), - tzHour = 0, - tzMin = 0; - if (match[9]) { - tzHour = toInt(match[9] + match[10]); - tzMin = toInt(match[9] + match[11]); - } - date.setUTCFullYear(toInt(match[1]), toInt(match[2]) - 1, toInt(match[3])); - date.setUTCHours(toInt(match[4] || 0) - tzHour, - toInt(match[5] || 0) - tzMin, - toInt(match[6] || 0), - toInt(match[7] || 0)); - return date; - } - return string; -} - -function toInt(str) { - return parseInt(str, 10); -} - -function padNumber(num, digits, trim) { - var neg = ''; - if (num < 0) { - neg = '-'; - num = -num; - } - num = '' + num; - while (num.length < digits) num = '0' + num; - if (trim) { - num = num.substr(num.length - digits); - } - return neg + num; -} - - -/** - * @ngdoc type - * @name angular.mock.TzDate - * @description - * - * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`. - * - * Mock of the Date type which has its timezone specified via constructor arg. - * - * The main purpose is to create Date-like instances with timezone fixed to the specified timezone - * offset, so that we can test code that depends on local timezone settings without dependency on - * the time zone settings of the machine where the code is running. - * - * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored) - * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC* - * - * @example - * !!!! WARNING !!!!! - * This is not a complete Date object so only methods that were implemented can be called safely. - * To make matters worse, TzDate instances inherit stuff from Date via a prototype. - * - * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is - * incomplete we might be missing some non-standard methods. This can result in errors like: - * "Date.prototype.foo called on incompatible Object". - * - * ```js - * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z'); - * newYearInBratislava.getTimezoneOffset() => -60; - * newYearInBratislava.getFullYear() => 2010; - * newYearInBratislava.getMonth() => 0; - * newYearInBratislava.getDate() => 1; - * newYearInBratislava.getHours() => 0; - * newYearInBratislava.getMinutes() => 0; - * newYearInBratislava.getSeconds() => 0; - * ``` - * - */ -angular.mock.TzDate = function(offset, timestamp) { - var self = new Date(0); - if (angular.isString(timestamp)) { - var tsStr = timestamp; - - self.origDate = jsonStringToDate(timestamp); - - timestamp = self.origDate.getTime(); - if (isNaN(timestamp)) { - throw { - name: "Illegal Argument", - message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string" - }; - } - } else { - self.origDate = new Date(timestamp); - } - - var localOffset = new Date(timestamp).getTimezoneOffset(); - self.offsetDiff = localOffset * 60 * 1000 - offset * 1000 * 60 * 60; - self.date = new Date(timestamp + self.offsetDiff); - - self.getTime = function() { - return self.date.getTime() - self.offsetDiff; - }; - - self.toLocaleDateString = function() { - return self.date.toLocaleDateString(); - }; - - self.getFullYear = function() { - return self.date.getFullYear(); - }; - - self.getMonth = function() { - return self.date.getMonth(); - }; - - self.getDate = function() { - return self.date.getDate(); - }; - - self.getHours = function() { - return self.date.getHours(); - }; - - self.getMinutes = function() { - return self.date.getMinutes(); - }; - - self.getSeconds = function() { - return self.date.getSeconds(); - }; - - self.getMilliseconds = function() { - return self.date.getMilliseconds(); - }; - - self.getTimezoneOffset = function() { - return offset * 60; - }; - - self.getUTCFullYear = function() { - return self.origDate.getUTCFullYear(); - }; - - self.getUTCMonth = function() { - return self.origDate.getUTCMonth(); - }; - - self.getUTCDate = function() { - return self.origDate.getUTCDate(); - }; - - self.getUTCHours = function() { - return self.origDate.getUTCHours(); - }; - - self.getUTCMinutes = function() { - return self.origDate.getUTCMinutes(); - }; - - self.getUTCSeconds = function() { - return self.origDate.getUTCSeconds(); - }; - - self.getUTCMilliseconds = function() { - return self.origDate.getUTCMilliseconds(); - }; - - self.getDay = function() { - return self.date.getDay(); - }; - - // provide this method only on browsers that already have it - if (self.toISOString) { - self.toISOString = function() { - return padNumber(self.origDate.getUTCFullYear(), 4) + '-' + - padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' + - padNumber(self.origDate.getUTCDate(), 2) + 'T' + - padNumber(self.origDate.getUTCHours(), 2) + ':' + - padNumber(self.origDate.getUTCMinutes(), 2) + ':' + - padNumber(self.origDate.getUTCSeconds(), 2) + '.' + - padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z'; - }; - } - - //hide all methods not implemented in this mock that the Date prototype exposes - var unimplementedMethods = ['getUTCDay', - 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', - 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear', - 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds', - 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString', - 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf']; - - angular.forEach(unimplementedMethods, function(methodName) { - self[methodName] = function() { - throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock"); - }; - }); - - return self; -}; - -//make "tzDateInstance instanceof Date" return true -angular.mock.TzDate.prototype = Date.prototype; -/* jshint +W101 */ - -angular.mock.animate = angular.module('ngAnimateMock', ['ng']) - - .config(['$provide', function($provide) { - - $provide.factory('$$forceReflow', function() { - function reflowFn() { - reflowFn.totalReflows++; - } - reflowFn.totalReflows = 0; - return reflowFn; - }); - - $provide.decorator('$animate', ['$delegate', '$timeout', '$browser', '$$rAF', '$$forceReflow', - function($delegate, $timeout, $browser, $$rAF, $$forceReflow) { - - var animate = { - queue: [], - cancel: $delegate.cancel, - get reflows() { - return $$forceReflow.totalReflows; - }, - enabled: $delegate.enabled, - triggerCallbackEvents: function() { - $$rAF.flush(); - }, - triggerCallbackPromise: function() { - $timeout.flush(0); - }, - triggerCallbacks: function() { - this.triggerCallbackEvents(); - this.triggerCallbackPromise(); - } - }; - - angular.forEach( - ['animate','enter','leave','move','addClass','removeClass','setClass'], function(method) { - animate[method] = function() { - animate.queue.push({ - event: method, - element: arguments[0], - options: arguments[arguments.length - 1], - args: arguments - }); - return $delegate[method].apply($delegate, arguments); - }; - }); - - return animate; - }]); - - }]); - - -/** - * @ngdoc function - * @name angular.mock.dump - * @description - * - * *NOTE*: this is not an injectable instance, just a globally available function. - * - * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for - * debugging. - * - * This method is also available on window, where it can be used to display objects on debug - * console. - * - * @param {*} object - any object to turn into string. - * @return {string} a serialized string of the argument - */ -angular.mock.dump = function(object) { - return serialize(object); - - function serialize(object) { - var out; - - if (angular.isElement(object)) { - object = angular.element(object); - out = angular.element('
    '); - angular.forEach(object, function(element) { - out.append(angular.element(element).clone()); - }); - out = out.html(); - } else if (angular.isArray(object)) { - out = []; - angular.forEach(object, function(o) { - out.push(serialize(o)); - }); - out = '[ ' + out.join(', ') + ' ]'; - } else if (angular.isObject(object)) { - if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) { - out = serializeScope(object); - } else if (object instanceof Error) { - out = object.stack || ('' + object.name + ': ' + object.message); - } else { - // TODO(i): this prevents methods being logged, - // we should have a better way to serialize objects - out = angular.toJson(object, true); - } - } else { - out = String(object); - } - - return out; - } - - function serializeScope(scope, offset) { - offset = offset || ' '; - var log = [offset + 'Scope(' + scope.$id + '): {']; - for (var key in scope) { - if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) { - log.push(' ' + key + ': ' + angular.toJson(scope[key])); - } - } - var child = scope.$$childHead; - while (child) { - log.push(serializeScope(child, offset + ' ')); - child = child.$$nextSibling; - } - log.push('}'); - return log.join('\n' + offset); - } -}; - -/** - * @ngdoc service - * @name $httpBackend - * @description - * Fake HTTP backend implementation suitable for unit testing applications that use the - * {@link ng.$http $http service}. - * - * *Note*: For fake HTTP backend implementation suitable for end-to-end testing or backend-less - * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}. - * - * During unit testing, we want our unit tests to run quickly and have no external dependencies so - * we don’t want to send [XHR](https://developer.mozilla.org/en/xmlhttprequest) or - * [JSONP](http://en.wikipedia.org/wiki/JSONP) requests to a real server. All we really need is - * to verify whether a certain request has been sent or not, or alternatively just let the - * application make requests, respond with pre-trained responses and assert that the end result is - * what we expect it to be. - * - * This mock implementation can be used to respond with static or dynamic responses via the - * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc). - * - * When an Angular application needs some data from a server, it calls the $http service, which - * sends the request to a real server using $httpBackend service. With dependency injection, it is - * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify - * the requests and respond with some testing data without sending a request to a real server. - * - * There are two ways to specify what test data should be returned as http responses by the mock - * backend when the code under test makes http requests: - * - * - `$httpBackend.expect` - specifies a request expectation - * - `$httpBackend.when` - specifies a backend definition - * - * - * # Request Expectations vs Backend Definitions - * - * Request expectations provide a way to make assertions about requests made by the application and - * to define responses for those requests. The test will fail if the expected requests are not made - * or they are made in the wrong order. - * - * Backend definitions allow you to define a fake backend for your application which doesn't assert - * if a particular request was made or not, it just returns a trained response if a request is made. - * The test will pass whether or not the request gets made during testing. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    Request expectationsBackend definitions
    Syntax.expect(...).respond(...).when(...).respond(...)
    Typical usagestrict unit testsloose (black-box) unit testing
    Fulfills multiple requestsNOYES
    Order of requests mattersYESNO
    Request requiredYESNO
    Response requiredoptional (see below)YES
    - * - * In cases where both backend definitions and request expectations are specified during unit - * testing, the request expectations are evaluated first. - * - * If a request expectation has no response specified, the algorithm will search your backend - * definitions for an appropriate response. - * - * If a request didn't match any expectation or if the expectation doesn't have the response - * defined, the backend definitions are evaluated in sequential order to see if any of them match - * the request. The response from the first matched definition is returned. - * - * - * # Flushing HTTP requests - * - * The $httpBackend used in production always responds to requests asynchronously. If we preserved - * this behavior in unit testing, we'd have to create async unit tests, which are hard to write, - * to follow and to maintain. But neither can the testing mock respond synchronously; that would - * change the execution of the code under test. For this reason, the mock $httpBackend has a - * `flush()` method, which allows the test to explicitly flush pending requests. This preserves - * the async api of the backend, while allowing the test to execute synchronously. - * - * - * # Unit testing with mock $httpBackend - * The following code shows how to setup and use the mock backend when unit testing a controller. - * First we create the controller under test: - * - ```js - // The module code - angular - .module('MyApp', []) - .controller('MyController', MyController); - - // The controller code - function MyController($scope, $http) { - var authToken; - - $http.get('/auth.py').success(function(data, status, headers) { - authToken = headers('A-Token'); - $scope.user = data; - }); - - $scope.saveMessage = function(message) { - var headers = { 'Authorization': authToken }; - $scope.status = 'Saving...'; - - $http.post('/add-msg.py', message, { headers: headers } ).success(function(response) { - $scope.status = ''; - }).error(function() { - $scope.status = 'ERROR!'; - }); - }; - } - ``` - * - * Now we setup the mock backend and create the test specs: - * - ```js - // testing controller - describe('MyController', function() { - var $httpBackend, $rootScope, createController, authRequestHandler; - - // Set up the module - beforeEach(module('MyApp')); - - beforeEach(inject(function($injector) { - // Set up the mock http service responses - $httpBackend = $injector.get('$httpBackend'); - // backend definition common for all tests - authRequestHandler = $httpBackend.when('GET', '/auth.py') - .respond({userId: 'userX'}, {'A-Token': 'xxx'}); - - // Get hold of a scope (i.e. the root scope) - $rootScope = $injector.get('$rootScope'); - // The $controller service is used to create instances of controllers - var $controller = $injector.get('$controller'); - - createController = function() { - return $controller('MyController', {'$scope' : $rootScope }); - }; - })); - - - afterEach(function() { - $httpBackend.verifyNoOutstandingExpectation(); - $httpBackend.verifyNoOutstandingRequest(); - }); - - - it('should fetch authentication token', function() { - $httpBackend.expectGET('/auth.py'); - var controller = createController(); - $httpBackend.flush(); - }); - - - it('should fail authentication', function() { - - // Notice how you can change the response even after it was set - authRequestHandler.respond(401, ''); - - $httpBackend.expectGET('/auth.py'); - var controller = createController(); - $httpBackend.flush(); - expect($rootScope.status).toBe('Failed...'); - }); - - - it('should send msg to server', function() { - var controller = createController(); - $httpBackend.flush(); - - // now you don’t care about the authentication, but - // the controller will still send the request and - // $httpBackend will respond without you having to - // specify the expectation and response for this request - - $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, ''); - $rootScope.saveMessage('message content'); - expect($rootScope.status).toBe('Saving...'); - $httpBackend.flush(); - expect($rootScope.status).toBe(''); - }); - - - it('should send auth header', function() { - var controller = createController(); - $httpBackend.flush(); - - $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) { - // check if the header was sent, if it wasn't the expectation won't - // match the request and the test will fail - return headers['Authorization'] == 'xxx'; - }).respond(201, ''); - - $rootScope.saveMessage('whatever'); - $httpBackend.flush(); - }); - }); - ``` - */ -angular.mock.$HttpBackendProvider = function() { - this.$get = ['$rootScope', '$timeout', createHttpBackendMock]; -}; - -/** - * General factory function for $httpBackend mock. - * Returns instance for unit testing (when no arguments specified): - * - passing through is disabled - * - auto flushing is disabled - * - * Returns instance for e2e testing (when `$delegate` and `$browser` specified): - * - passing through (delegating request to real backend) is enabled - * - auto flushing is enabled - * - * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified) - * @param {Object=} $browser Auto-flushing enabled if specified - * @return {Object} Instance of $httpBackend mock - */ -function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { - var definitions = [], - expectations = [], - responses = [], - responsesPush = angular.bind(responses, responses.push), - copy = angular.copy; - - function createResponse(status, data, headers, statusText) { - if (angular.isFunction(status)) return status; - - return function() { - return angular.isNumber(status) - ? [status, data, headers, statusText] - : [200, status, data, headers]; - }; - } - - // TODO(vojta): change params to: method, url, data, headers, callback - function $httpBackend(method, url, data, callback, headers, timeout, withCredentials) { - var xhr = new MockXhr(), - expectation = expectations[0], - wasExpected = false; - - function prettyPrint(data) { - return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp) - ? data - : angular.toJson(data); - } - - function wrapResponse(wrapped) { - if (!$browser && timeout) { - timeout.then ? timeout.then(handleTimeout) : $timeout(handleTimeout, timeout); - } - - return handleResponse; - - function handleResponse() { - var response = wrapped.response(method, url, data, headers); - xhr.$$respHeaders = response[2]; - callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(), - copy(response[3] || '')); - } - - function handleTimeout() { - for (var i = 0, ii = responses.length; i < ii; i++) { - if (responses[i] === handleResponse) { - responses.splice(i, 1); - callback(-1, undefined, ''); - break; - } - } - } - } - - if (expectation && expectation.match(method, url)) { - if (!expectation.matchData(data)) { - throw new Error('Expected ' + expectation + ' with different data\n' + - 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data); - } - - if (!expectation.matchHeaders(headers)) { - throw new Error('Expected ' + expectation + ' with different headers\n' + - 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' + - prettyPrint(headers)); - } - - expectations.shift(); - - if (expectation.response) { - responses.push(wrapResponse(expectation)); - return; - } - wasExpected = true; - } - - var i = -1, definition; - while ((definition = definitions[++i])) { - if (definition.match(method, url, data, headers || {})) { - if (definition.response) { - // if $browser specified, we do auto flush all requests - ($browser ? $browser.defer : responsesPush)(wrapResponse(definition)); - } else if (definition.passThrough) { - $delegate(method, url, data, callback, headers, timeout, withCredentials); - } else throw new Error('No response defined !'); - return; - } - } - throw wasExpected ? - new Error('No response defined !') : - new Error('Unexpected request: ' + method + ' ' + url + '\n' + - (expectation ? 'Expected ' + expectation : 'No more request expected')); - } - - /** - * @ngdoc method - * @name $httpBackend#when - * @description - * Creates a new backend definition. - * - * @param {string} method HTTP method. - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives - * data string and returns true if the data is as expected. - * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header - * object and returns true if the headers match the current definition. - * @returns {requestHandler} Returns an object with `respond` method that controls how a matched - * request is handled. You can save this object for later use and invoke `respond` again in - * order to change how a matched request is handled. - * - * - respond – - * `{function([status,] data[, headers, statusText]) - * | function(function(method, url, data, headers)}` - * – The respond method takes a set of static data to be returned or a function that can - * return an array containing response status (number), response data (string), response - * headers (Object), and the text for the status (string). The respond method returns the - * `requestHandler` object for possible overrides. - */ - $httpBackend.when = function(method, url, data, headers) { - var definition = new MockHttpExpectation(method, url, data, headers), - chain = { - respond: function(status, data, headers, statusText) { - definition.passThrough = undefined; - definition.response = createResponse(status, data, headers, statusText); - return chain; - } - }; - - if ($browser) { - chain.passThrough = function() { - definition.response = undefined; - definition.passThrough = true; - return chain; - }; - } - - definitions.push(definition); - return chain; - }; - - /** - * @ngdoc method - * @name $httpBackend#whenGET - * @description - * Creates a new backend definition for GET requests. For more info see `when()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that controls how a matched - * request is handled. You can save this object for later use and invoke `respond` again in - * order to change how a matched request is handled. - */ - - /** - * @ngdoc method - * @name $httpBackend#whenHEAD - * @description - * Creates a new backend definition for HEAD requests. For more info see `when()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that controls how a matched - * request is handled. You can save this object for later use and invoke `respond` again in - * order to change how a matched request is handled. - */ - - /** - * @ngdoc method - * @name $httpBackend#whenDELETE - * @description - * Creates a new backend definition for DELETE requests. For more info see `when()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that controls how a matched - * request is handled. You can save this object for later use and invoke `respond` again in - * order to change how a matched request is handled. - */ - - /** - * @ngdoc method - * @name $httpBackend#whenPOST - * @description - * Creates a new backend definition for POST requests. For more info see `when()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives - * data string and returns true if the data is as expected. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that controls how a matched - * request is handled. You can save this object for later use and invoke `respond` again in - * order to change how a matched request is handled. - */ - - /** - * @ngdoc method - * @name $httpBackend#whenPUT - * @description - * Creates a new backend definition for PUT requests. For more info see `when()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives - * data string and returns true if the data is as expected. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that controls how a matched - * request is handled. You can save this object for later use and invoke `respond` again in - * order to change how a matched request is handled. - */ - - /** - * @ngdoc method - * @name $httpBackend#whenJSONP - * @description - * Creates a new backend definition for JSONP requests. For more info see `when()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @returns {requestHandler} Returns an object with `respond` method that controls how a matched - * request is handled. You can save this object for later use and invoke `respond` again in - * order to change how a matched request is handled. - */ - createShortMethods('when'); - - - /** - * @ngdoc method - * @name $httpBackend#expect - * @description - * Creates a new request expectation. - * - * @param {string} method HTTP method. - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that - * receives data string and returns true if the data is as expected, or Object if request body - * is in JSON format. - * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header - * object and returns true if the headers match the current expectation. - * @returns {requestHandler} Returns an object with `respond` method that controls how a matched - * request is handled. You can save this object for later use and invoke `respond` again in - * order to change how a matched request is handled. - * - * - respond – - * `{function([status,] data[, headers, statusText]) - * | function(function(method, url, data, headers)}` - * – The respond method takes a set of static data to be returned or a function that can - * return an array containing response status (number), response data (string), response - * headers (Object), and the text for the status (string). The respond method returns the - * `requestHandler` object for possible overrides. - */ - $httpBackend.expect = function(method, url, data, headers) { - var expectation = new MockHttpExpectation(method, url, data, headers), - chain = { - respond: function(status, data, headers, statusText) { - expectation.response = createResponse(status, data, headers, statusText); - return chain; - } - }; - - expectations.push(expectation); - return chain; - }; - - - /** - * @ngdoc method - * @name $httpBackend#expectGET - * @description - * Creates a new request expectation for GET requests. For more info see `expect()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that controls how a matched - * request is handled. You can save this object for later use and invoke `respond` again in - * order to change how a matched request is handled. See #expect for more info. - */ - - /** - * @ngdoc method - * @name $httpBackend#expectHEAD - * @description - * Creates a new request expectation for HEAD requests. For more info see `expect()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that controls how a matched - * request is handled. You can save this object for later use and invoke `respond` again in - * order to change how a matched request is handled. - */ - - /** - * @ngdoc method - * @name $httpBackend#expectDELETE - * @description - * Creates a new request expectation for DELETE requests. For more info see `expect()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that controls how a matched - * request is handled. You can save this object for later use and invoke `respond` again in - * order to change how a matched request is handled. - */ - - /** - * @ngdoc method - * @name $httpBackend#expectPOST - * @description - * Creates a new request expectation for POST requests. For more info see `expect()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that - * receives data string and returns true if the data is as expected, or Object if request body - * is in JSON format. - * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that controls how a matched - * request is handled. You can save this object for later use and invoke `respond` again in - * order to change how a matched request is handled. - */ - - /** - * @ngdoc method - * @name $httpBackend#expectPUT - * @description - * Creates a new request expectation for PUT requests. For more info see `expect()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that - * receives data string and returns true if the data is as expected, or Object if request body - * is in JSON format. - * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that controls how a matched - * request is handled. You can save this object for later use and invoke `respond` again in - * order to change how a matched request is handled. - */ - - /** - * @ngdoc method - * @name $httpBackend#expectPATCH - * @description - * Creates a new request expectation for PATCH requests. For more info see `expect()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that - * receives data string and returns true if the data is as expected, or Object if request body - * is in JSON format. - * @param {Object=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` method that controls how a matched - * request is handled. You can save this object for later use and invoke `respond` again in - * order to change how a matched request is handled. - */ - - /** - * @ngdoc method - * @name $httpBackend#expectJSONP - * @description - * Creates a new request expectation for JSONP requests. For more info see `expect()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives an url - * and returns true if the url matches the current definition. - * @returns {requestHandler} Returns an object with `respond` method that controls how a matched - * request is handled. You can save this object for later use and invoke `respond` again in - * order to change how a matched request is handled. - */ - createShortMethods('expect'); - - - /** - * @ngdoc method - * @name $httpBackend#flush - * @description - * Flushes all pending requests using the trained responses. - * - * @param {number=} count Number of responses to flush (in the order they arrived). If undefined, - * all pending requests will be flushed. If there are no pending requests when the flush method - * is called an exception is thrown (as this typically a sign of programming error). - */ - $httpBackend.flush = function(count, digest) { - if (digest !== false) $rootScope.$digest(); - if (!responses.length) throw new Error('No pending request to flush !'); - - if (angular.isDefined(count) && count !== null) { - while (count--) { - if (!responses.length) throw new Error('No more pending request to flush !'); - responses.shift()(); - } - } else { - while (responses.length) { - responses.shift()(); - } - } - $httpBackend.verifyNoOutstandingExpectation(digest); - }; - - - /** - * @ngdoc method - * @name $httpBackend#verifyNoOutstandingExpectation - * @description - * Verifies that all of the requests defined via the `expect` api were made. If any of the - * requests were not made, verifyNoOutstandingExpectation throws an exception. - * - * Typically, you would call this method following each test case that asserts requests using an - * "afterEach" clause. - * - * ```js - * afterEach($httpBackend.verifyNoOutstandingExpectation); - * ``` - */ - $httpBackend.verifyNoOutstandingExpectation = function(digest) { - if (digest !== false) $rootScope.$digest(); - if (expectations.length) { - throw new Error('Unsatisfied requests: ' + expectations.join(', ')); - } - }; - - - /** - * @ngdoc method - * @name $httpBackend#verifyNoOutstandingRequest - * @description - * Verifies that there are no outstanding requests that need to be flushed. - * - * Typically, you would call this method following each test case that asserts requests using an - * "afterEach" clause. - * - * ```js - * afterEach($httpBackend.verifyNoOutstandingRequest); - * ``` - */ - $httpBackend.verifyNoOutstandingRequest = function() { - if (responses.length) { - throw new Error('Unflushed requests: ' + responses.length); - } - }; - - - /** - * @ngdoc method - * @name $httpBackend#resetExpectations - * @description - * Resets all request expectations, but preserves all backend definitions. Typically, you would - * call resetExpectations during a multiple-phase test when you want to reuse the same instance of - * $httpBackend mock. - */ - $httpBackend.resetExpectations = function() { - expectations.length = 0; - responses.length = 0; - }; - - return $httpBackend; - - - function createShortMethods(prefix) { - angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) { - $httpBackend[prefix + method] = function(url, headers) { - return $httpBackend[prefix](method, url, undefined, headers); - }; - }); - - angular.forEach(['PUT', 'POST', 'PATCH'], function(method) { - $httpBackend[prefix + method] = function(url, data, headers) { - return $httpBackend[prefix](method, url, data, headers); - }; - }); - } -} - -function MockHttpExpectation(method, url, data, headers) { - - this.data = data; - this.headers = headers; - - this.match = function(m, u, d, h) { - if (method != m) return false; - if (!this.matchUrl(u)) return false; - if (angular.isDefined(d) && !this.matchData(d)) return false; - if (angular.isDefined(h) && !this.matchHeaders(h)) return false; - return true; - }; - - this.matchUrl = function(u) { - if (!url) return true; - if (angular.isFunction(url.test)) return url.test(u); - if (angular.isFunction(url)) return url(u); - return url == u; - }; - - this.matchHeaders = function(h) { - if (angular.isUndefined(headers)) return true; - if (angular.isFunction(headers)) return headers(h); - return angular.equals(headers, h); - }; - - this.matchData = function(d) { - if (angular.isUndefined(data)) return true; - if (data && angular.isFunction(data.test)) return data.test(d); - if (data && angular.isFunction(data)) return data(d); - if (data && !angular.isString(data)) { - return angular.equals(angular.fromJson(angular.toJson(data)), angular.fromJson(d)); - } - return data == d; - }; - - this.toString = function() { - return method + ' ' + url; - }; -} - -function createMockXhr() { - return new MockXhr(); -} - -function MockXhr() { - - // hack for testing $http, $httpBackend - MockXhr.$$lastInstance = this; - - this.open = function(method, url, async) { - this.$$method = method; - this.$$url = url; - this.$$async = async; - this.$$reqHeaders = {}; - this.$$respHeaders = {}; - }; - - this.send = function(data) { - this.$$data = data; - }; - - this.setRequestHeader = function(key, value) { - this.$$reqHeaders[key] = value; - }; - - this.getResponseHeader = function(name) { - // the lookup must be case insensitive, - // that's why we try two quick lookups first and full scan last - var header = this.$$respHeaders[name]; - if (header) return header; - - name = angular.lowercase(name); - header = this.$$respHeaders[name]; - if (header) return header; - - header = undefined; - angular.forEach(this.$$respHeaders, function(headerVal, headerName) { - if (!header && angular.lowercase(headerName) == name) header = headerVal; - }); - return header; - }; - - this.getAllResponseHeaders = function() { - var lines = []; - - angular.forEach(this.$$respHeaders, function(value, key) { - lines.push(key + ': ' + value); - }); - return lines.join('\n'); - }; - - this.abort = angular.noop; -} - - -/** - * @ngdoc service - * @name $timeout - * @description - * - * This service is just a simple decorator for {@link ng.$timeout $timeout} service - * that adds a "flush" and "verifyNoPendingTasks" methods. - */ - -angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $browser) { - - /** - * @ngdoc method - * @name $timeout#flush - * @description - * - * Flushes the queue of pending tasks. - * - * @param {number=} delay maximum timeout amount to flush up until - */ - $delegate.flush = function(delay) { - $browser.defer.flush(delay); - }; - - /** - * @ngdoc method - * @name $timeout#verifyNoPendingTasks - * @description - * - * Verifies that there are no pending tasks that need to be flushed. - */ - $delegate.verifyNoPendingTasks = function() { - if ($browser.deferredFns.length) { - throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' + - formatPendingTasksAsString($browser.deferredFns)); - } - }; - - function formatPendingTasksAsString(tasks) { - var result = []; - angular.forEach(tasks, function(task) { - result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}'); - }); - - return result.join(', '); - } - - return $delegate; -}]; - -angular.mock.$RAFDecorator = ['$delegate', function($delegate) { - var queue = []; - var rafFn = function(fn) { - var index = queue.length; - queue.push(fn); - return function() { - queue.splice(index, 1); - }; - }; - - rafFn.supported = $delegate.supported; - - rafFn.flush = function() { - if (queue.length === 0) { - throw new Error('No rAF callbacks present'); - } - - var length = queue.length; - for (var i = 0; i < length; i++) { - queue[i](); - } - - queue = queue.slice(i); - }; - - return rafFn; -}]; - -/** - * - */ -angular.mock.$RootElementProvider = function() { - this.$get = function() { - return angular.element('
    '); - }; -}; - -/** - * @ngdoc service - * @name $controller - * @description - * A decorator for {@link ng.$controller} with additional `bindings` parameter, useful when testing - * controllers of directives that use {@link $compile#-bindtocontroller- `bindToController`}. - * - * - * ## Example - * - * ```js - * - * // Directive definition ... - * - * myMod.directive('myDirective', { - * controller: 'MyDirectiveController', - * bindToController: { - * name: '@' - * } - * }); - * - * - * // Controller definition ... - * - * myMod.controller('MyDirectiveController', ['log', function($log) { - * $log.info(this.name); - * })]; - * - * - * // In a test ... - * - * describe('myDirectiveController', function() { - * it('should write the bound name to the log', inject(function($controller, $log) { - * var ctrl = $controller('MyDirective', { /* no locals */ }, { name: 'Clark Kent' }); - * expect(ctrl.name).toEqual('Clark Kent'); - * expect($log.info.logs).toEqual(['Clark Kent']); - * }); - * }); - * - * ``` - * - * @param {Function|string} constructor If called with a function then it's considered to be the - * controller constructor function. Otherwise it's considered to be a string which is used - * to retrieve the controller constructor using the following steps: - * - * * check if a controller with given name is registered via `$controllerProvider` - * * check if evaluating the string on the current scope returns a constructor - * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global - * `window` object (not recommended) - * - * The string can use the `controller as property` syntax, where the controller instance is published - * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this - * to work correctly. - * - * @param {Object} locals Injection locals for Controller. - * @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used - * to simulate the `bindToController` feature and simplify certain kinds of tests. - * @return {Object} Instance of given controller. - */ -angular.mock.$ControllerDecorator = ['$delegate', function($delegate) { - return function(expression, locals, later, ident) { - if (later && typeof later === 'object') { - var create = $delegate(expression, locals, true, ident); - angular.extend(create.instance, later); - return create(); - } - return $delegate(expression, locals, later, ident); - }; -}]; - - -/** - * @ngdoc module - * @name ngMock - * @packageName angular-mocks - * @description - * - * # ngMock - * - * The `ngMock` module provides support to inject and mock Angular services into unit tests. - * In addition, ngMock also extends various core ng services such that they can be - * inspected and controlled in a synchronous manner within test code. - * - * - *
    - * - */ -angular.module('ngMock', ['ng']).provider({ - $browser: angular.mock.$BrowserProvider, - $exceptionHandler: angular.mock.$ExceptionHandlerProvider, - $log: angular.mock.$LogProvider, - $interval: angular.mock.$IntervalProvider, - $httpBackend: angular.mock.$HttpBackendProvider, - $rootElement: angular.mock.$RootElementProvider -}).config(['$provide', function($provide) { - $provide.decorator('$timeout', angular.mock.$TimeoutDecorator); - $provide.decorator('$$rAF', angular.mock.$RAFDecorator); - $provide.decorator('$rootScope', angular.mock.$RootScopeDecorator); - $provide.decorator('$controller', angular.mock.$ControllerDecorator); -}]); - -/** - * @ngdoc module - * @name ngMockE2E - * @module ngMockE2E - * @packageName angular-mocks - * @description - * - * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing. - * Currently there is only one mock present in this module - - * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock. - */ -angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { - $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator); -}]); - -/** - * @ngdoc service - * @name $httpBackend - * @module ngMockE2E - * @description - * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of - * applications that use the {@link ng.$http $http service}. - * - * *Note*: For fake http backend implementation suitable for unit testing please see - * {@link ngMock.$httpBackend unit-testing $httpBackend mock}. - * - * This implementation can be used to respond with static or dynamic responses via the `when` api - * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the - * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch - * templates from a webserver). - * - * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application - * is being developed with the real backend api replaced with a mock, it is often desirable for - * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch - * templates or static files from the webserver). To configure the backend with this behavior - * use the `passThrough` request handler of `when` instead of `respond`. - * - * Additionally, we don't want to manually have to flush mocked out requests like we do during unit - * testing. For this reason the e2e $httpBackend flushes mocked out requests - * automatically, closely simulating the behavior of the XMLHttpRequest object. - * - * To setup the application to run with this http backend, you have to create a module that depends - * on the `ngMockE2E` and your application modules and defines the fake backend: - * - * ```js - * myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']); - * myAppDev.run(function($httpBackend) { - * phones = [{name: 'phone1'}, {name: 'phone2'}]; - * - * // returns the current list of phones - * $httpBackend.whenGET('/phones').respond(phones); - * - * // adds a new phone to the phones array - * $httpBackend.whenPOST('/phones').respond(function(method, url, data) { - * var phone = angular.fromJson(data); - * phones.push(phone); - * return [200, phone, {}]; - * }); - * $httpBackend.whenGET(/^\/templates\//).passThrough(); - * //... - * }); - * ``` - * - * Afterwards, bootstrap your app with this new module. - */ - -/** - * @ngdoc method - * @name $httpBackend#when - * @module ngMockE2E - * @description - * Creates a new backend definition. - * - * @param {string} method HTTP method. - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(string|RegExp)=} data HTTP request body. - * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header - * object and returns true if the headers match the current definition. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. You can save this object for later use and invoke - * `respond` or `passThrough` again in order to change how a matched request is handled. - * - * - respond – - * `{function([status,] data[, headers, statusText]) - * | function(function(method, url, data, headers)}` - * – The respond method takes a set of static data to be returned or a function that can return - * an array containing response status (number), response data (string), response headers - * (Object), and the text for the status (string). - * - passThrough – `{function()}` – Any request matching a backend definition with - * `passThrough` handler will be passed through to the real backend (an XHR request will be made - * to the server.) - * - Both methods return the `requestHandler` object for possible overrides. - */ - -/** - * @ngdoc method - * @name $httpBackend#whenGET - * @module ngMockE2E - * @description - * Creates a new backend definition for GET requests. For more info see `when()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. You can save this object for later use and invoke - * `respond` or `passThrough` again in order to change how a matched request is handled. - */ - -/** - * @ngdoc method - * @name $httpBackend#whenHEAD - * @module ngMockE2E - * @description - * Creates a new backend definition for HEAD requests. For more info see `when()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. You can save this object for later use and invoke - * `respond` or `passThrough` again in order to change how a matched request is handled. - */ - -/** - * @ngdoc method - * @name $httpBackend#whenDELETE - * @module ngMockE2E - * @description - * Creates a new backend definition for DELETE requests. For more info see `when()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. You can save this object for later use and invoke - * `respond` or `passThrough` again in order to change how a matched request is handled. - */ - -/** - * @ngdoc method - * @name $httpBackend#whenPOST - * @module ngMockE2E - * @description - * Creates a new backend definition for POST requests. For more info see `when()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(string|RegExp)=} data HTTP request body. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. You can save this object for later use and invoke - * `respond` or `passThrough` again in order to change how a matched request is handled. - */ - -/** - * @ngdoc method - * @name $httpBackend#whenPUT - * @module ngMockE2E - * @description - * Creates a new backend definition for PUT requests. For more info see `when()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(string|RegExp)=} data HTTP request body. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. You can save this object for later use and invoke - * `respond` or `passThrough` again in order to change how a matched request is handled. - */ - -/** - * @ngdoc method - * @name $httpBackend#whenPATCH - * @module ngMockE2E - * @description - * Creates a new backend definition for PATCH requests. For more info see `when()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @param {(string|RegExp)=} data HTTP request body. - * @param {(Object|function(Object))=} headers HTTP headers. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. You can save this object for later use and invoke - * `respond` or `passThrough` again in order to change how a matched request is handled. - */ - -/** - * @ngdoc method - * @name $httpBackend#whenJSONP - * @module ngMockE2E - * @description - * Creates a new backend definition for JSONP requests. For more info see `when()`. - * - * @param {string|RegExp|function(string)} url HTTP url or function that receives a url - * and returns true if the url matches the current definition. - * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that - * control how a matched request is handled. You can save this object for later use and invoke - * `respond` or `passThrough` again in order to change how a matched request is handled. - */ -angular.mock.e2e = {}; -angular.mock.e2e.$httpBackendDecorator = - ['$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock]; - - -/** - * @ngdoc type - * @name $rootScope.Scope - * @module ngMock - * @description - * {@link ng.$rootScope.Scope Scope} type decorated with helper methods useful for testing. These - * methods are automatically available on any {@link ng.$rootScope.Scope Scope} instance when - * `ngMock` module is loaded. - * - * In addition to all the regular `Scope` methods, the following helper methods are available: - */ -angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) { - - var $rootScopePrototype = Object.getPrototypeOf($delegate); - - $rootScopePrototype.$countChildScopes = countChildScopes; - $rootScopePrototype.$countWatchers = countWatchers; - - return $delegate; - - // ------------------------------------------------------------------------------------------ // - - /** - * @ngdoc method - * @name $rootScope.Scope#$countChildScopes - * @module ngMock - * @description - * Counts all the direct and indirect child scopes of the current scope. - * - * The current scope is excluded from the count. The count includes all isolate child scopes. - * - * @returns {number} Total number of child scopes. - */ - function countChildScopes() { - // jshint validthis: true - var count = 0; // exclude the current scope - var pendingChildHeads = [this.$$childHead]; - var currentScope; - - while (pendingChildHeads.length) { - currentScope = pendingChildHeads.shift(); - - while (currentScope) { - count += 1; - pendingChildHeads.push(currentScope.$$childHead); - currentScope = currentScope.$$nextSibling; - } - } - - return count; - } - - - /** - * @ngdoc method - * @name $rootScope.Scope#$countWatchers - * @module ngMock - * @description - * Counts all the watchers of direct and indirect child scopes of the current scope. - * - * The watchers of the current scope are included in the count and so are all the watchers of - * isolate child scopes. - * - * @returns {number} Total number of watchers. - */ - function countWatchers() { - // jshint validthis: true - var count = this.$$watchers ? this.$$watchers.length : 0; // include the current scope - var pendingChildHeads = [this.$$childHead]; - var currentScope; - - while (pendingChildHeads.length) { - currentScope = pendingChildHeads.shift(); - - while (currentScope) { - count += currentScope.$$watchers ? currentScope.$$watchers.length : 0; - pendingChildHeads.push(currentScope.$$childHead); - currentScope = currentScope.$$nextSibling; - } - } - - return count; - } -}]; - - -if (window.jasmine || window.mocha) { - - var currentSpec = null, - annotatedFunctions = [], - isSpecRunning = function() { - return !!currentSpec; - }; - - angular.mock.$$annotate = angular.injector.$$annotate; - angular.injector.$$annotate = function(fn) { - if (typeof fn === 'function' && !fn.$inject) { - annotatedFunctions.push(fn); - } - return angular.mock.$$annotate.apply(this, arguments); - }; - - - (window.beforeEach || window.setup)(function() { - annotatedFunctions = []; - currentSpec = this; - }); - - (window.afterEach || window.teardown)(function() { - var injector = currentSpec.$injector; - - annotatedFunctions.forEach(function(fn) { - delete fn.$inject; - }); - - angular.forEach(currentSpec.$modules, function(module) { - if (module && module.$$hashKey) { - module.$$hashKey = undefined; - } - }); - - currentSpec.$injector = null; - currentSpec.$modules = null; - currentSpec = null; - - if (injector) { - injector.get('$rootElement').off(); - } - - // clean up jquery's fragment cache - angular.forEach(angular.element.fragments, function(val, key) { - delete angular.element.fragments[key]; - }); - - MockXhr.$$lastInstance = null; - - angular.forEach(angular.callbacks, function(val, key) { - delete angular.callbacks[key]; - }); - angular.callbacks.counter = 0; - }); - - /** - * @ngdoc function - * @name angular.mock.module - * @description - * - * *NOTE*: This function is also published on window for easy access.
    - * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha - * - * This function registers a module configuration code. It collects the configuration information - * which will be used when the injector is created by {@link angular.mock.inject inject}. - * - * See {@link angular.mock.inject inject} for usage example - * - * @param {...(string|Function|Object)} fns any number of modules which are represented as string - * aliases or as anonymous module initialization functions. The modules are used to - * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an - * object literal is passed they will be registered as values in the module, the key being - * the module name and the value being what is returned. - */ - window.module = angular.mock.module = function() { - var moduleFns = Array.prototype.slice.call(arguments, 0); - return isSpecRunning() ? workFn() : workFn; - ///////////////////// - function workFn() { - if (currentSpec.$injector) { - throw new Error('Injector already created, can not register a module!'); - } else { - var modules = currentSpec.$modules || (currentSpec.$modules = []); - angular.forEach(moduleFns, function(module) { - if (angular.isObject(module) && !angular.isArray(module)) { - modules.push(function($provide) { - angular.forEach(module, function(value, key) { - $provide.value(key, value); - }); - }); - } else { - modules.push(module); - } - }); - } - } - }; - - /** - * @ngdoc function - * @name angular.mock.inject - * @description - * - * *NOTE*: This function is also published on window for easy access.
    - * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha - * - * The inject function wraps a function into an injectable function. The inject() creates new - * instance of {@link auto.$injector $injector} per test, which is then used for - * resolving references. - * - * - * ## Resolving References (Underscore Wrapping) - * Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this - * in multiple `it()` clauses. To be able to do this we must assign the reference to a variable - * that is declared in the scope of the `describe()` block. Since we would, most likely, want - * the variable to have the same name of the reference we have a problem, since the parameter - * to the `inject()` function would hide the outer variable. - * - * To help with this, the injected parameters can, optionally, be enclosed with underscores. - * These are ignored by the injector when the reference name is resolved. - * - * For example, the parameter `_myService_` would be resolved as the reference `myService`. - * Since it is available in the function body as _myService_, we can then assign it to a variable - * defined in an outer scope. - * - * ``` - * // Defined out reference variable outside - * var myService; - * - * // Wrap the parameter in underscores - * beforeEach( inject( function(_myService_){ - * myService = _myService_; - * })); - * - * // Use myService in a series of tests. - * it('makes use of myService', function() { - * myService.doStuff(); - * }); - * - * ``` - * - * See also {@link angular.mock.module angular.mock.module} - * - * ## Example - * Example of what a typical jasmine tests looks like with the inject method. - * ```js - * - * angular.module('myApplicationModule', []) - * .value('mode', 'app') - * .value('version', 'v1.0.1'); - * - * - * describe('MyApp', function() { - * - * // You need to load modules that you want to test, - * // it loads only the "ng" module by default. - * beforeEach(module('myApplicationModule')); - * - * - * // inject() is used to inject arguments of all given functions - * it('should provide a version', inject(function(mode, version) { - * expect(version).toEqual('v1.0.1'); - * expect(mode).toEqual('app'); - * })); - * - * - * // The inject and module method can also be used inside of the it or beforeEach - * it('should override a version and test the new version is injected', function() { - * // module() takes functions or strings (module aliases) - * module(function($provide) { - * $provide.value('version', 'overridden'); // override version here - * }); - * - * inject(function(version) { - * expect(version).toEqual('overridden'); - * }); - * }); - * }); - * - * ``` - * - * @param {...Function} fns any number of functions which will be injected using the injector. - */ - - - - var ErrorAddingDeclarationLocationStack = function(e, errorForStack) { - this.message = e.message; - this.name = e.name; - if (e.line) this.line = e.line; - if (e.sourceId) this.sourceId = e.sourceId; - if (e.stack && errorForStack) - this.stack = e.stack + '\n' + errorForStack.stack; - if (e.stackArray) this.stackArray = e.stackArray; - }; - ErrorAddingDeclarationLocationStack.prototype.toString = Error.prototype.toString; - - window.inject = angular.mock.inject = function() { - var blockFns = Array.prototype.slice.call(arguments, 0); - var errorForStack = new Error('Declaration Location'); - return isSpecRunning() ? workFn.call(currentSpec) : workFn; - ///////////////////// - function workFn() { - var modules = currentSpec.$modules || []; - var strictDi = !!currentSpec.$injectorStrict; - modules.unshift('ngMock'); - modules.unshift('ng'); - var injector = currentSpec.$injector; - if (!injector) { - if (strictDi) { - // If strictDi is enabled, annotate the providerInjector blocks - angular.forEach(modules, function(moduleFn) { - if (typeof moduleFn === "function") { - angular.injector.$$annotate(moduleFn); - } - }); - } - injector = currentSpec.$injector = angular.injector(modules, strictDi); - currentSpec.$injectorStrict = strictDi; - } - for (var i = 0, ii = blockFns.length; i < ii; i++) { - if (currentSpec.$injectorStrict) { - // If the injector is strict / strictDi, and the spec wants to inject using automatic - // annotation, then annotate the function here. - injector.annotate(blockFns[i]); - } - try { - /* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */ - injector.invoke(blockFns[i] || angular.noop, this); - /* jshint +W040 */ - } catch (e) { - if (e.stack && errorForStack) { - throw new ErrorAddingDeclarationLocationStack(e, errorForStack); - } - throw e; - } finally { - errorForStack = null; - } - } - } - }; - - - angular.mock.inject.strictDi = function(value) { - value = arguments.length ? !!value : true; - return isSpecRunning() ? workFn() : workFn; - - function workFn() { - if (value !== currentSpec.$injectorStrict) { - if (currentSpec.$injector) { - throw new Error('Injector already created, can not modify strict annotations'); - } else { - currentSpec.$injectorStrict = value; - } - } - } - }; -} - - -})(window, window.angular); diff --git a/themes/src/main/resources/theme/keycloak/common/resources/lib/angular/angular-scenario.js b/themes/src/main/resources/theme/keycloak/common/resources/lib/angular/angular-scenario.js deleted file mode 100644 index 705d70e65e..0000000000 --- a/themes/src/main/resources/theme/keycloak/common/resources/lib/angular/angular-scenario.js +++ /dev/null @@ -1,40024 +0,0 @@ -/*! - * jQuery JavaScript Library v2.1.1 - * http://jquery.com/ - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * - * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2014-05-01T17:11Z - */ - -(function( global, factory ) {'use strict'; - - if ( typeof module === "object" && typeof module.exports === "object" ) { - // For CommonJS and CommonJS-like environments where a proper window is present, - // execute the factory and get jQuery - // For environments that do not inherently posses a window with a document - // (such as Node.js), expose a jQuery-making factory as module.exports - // This accentuates the need for the creation of a real window - // e.g. var jQuery = require("jquery")(window); - // See ticket #14549 for more info - module.exports = global.document ? - factory( global, true ) : - function( w ) { - if ( !w.document ) { - throw new Error( "jQuery requires a window with a document" ); - } - return factory( w ); - }; - } else { - factory( global ); - } - -// Pass this if window is not defined yet -}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { - -// Can't do this because several apps including ASP.NET trace -// the stack via arguments.caller.callee and Firefox dies if -// you try to trace through "use strict" call chains. (#13335) -// Support: Firefox 18+ -// - -var arr = []; - -var slice = arr.slice; - -var concat = arr.concat; - -var push = arr.push; - -var indexOf = arr.indexOf; - -var class2type = {}; - -var toString = class2type.toString; - -var hasOwn = class2type.hasOwnProperty; - -var support = {}; - - - -var - // Use the correct document accordingly with window argument (sandbox) - document = window.document, - - version = "2.1.1", - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - // Need init if jQuery is called (just allow error to be thrown if not included) - return new jQuery.fn.init( selector, context ); - }, - - // Support: Android<4.1 - // Make sure we trim BOM and NBSP - rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, - - // Matches dashed string for camelizing - rmsPrefix = /^-ms-/, - rdashAlpha = /-([\da-z])/gi, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return letter.toUpperCase(); - }; - -jQuery.fn = jQuery.prototype = { - // The current version of jQuery being used - jquery: version, - - constructor: jQuery, - - // Start with an empty selector - selector: "", - - // The default length of a jQuery object is 0 - length: 0, - - toArray: function() { - return slice.call( this ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num != null ? - - // Return just the one element from the set - ( num < 0 ? this[ num + this.length ] : this[ num ] ) : - - // Return all the elements in a clean array - slice.call( this ); - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - ret.context = this.context; - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ) ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - eq: function( i ) { - var len = this.length, - j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); - }, - - end: function() { - return this.prevObject || this.constructor(null); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: arr.sort, - splice: arr.splice -}; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - - // skip the boolean and the target - target = arguments[ i ] || {}; - i++; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } - - // extend jQuery itself if only one argument is passed - if ( i === length ) { - target = this; - i--; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; - - } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend({ - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - - // Assume jQuery is ready without the ready module - isReady: true, - - error: function( msg ) { - throw new Error( msg ); - }, - - noop: function() {}, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return jQuery.type(obj) === "function"; - }, - - isArray: Array.isArray, - - isWindow: function( obj ) { - return obj != null && obj === obj.window; - }, - - isNumeric: function( obj ) { - // parseFloat NaNs numeric-cast false positives (null|true|false|"") - // ...but misinterprets leading-number strings, particularly hex literals ("0x...") - // subtraction forces infinities to NaN - return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0; - }, - - isPlainObject: function( obj ) { - // Not plain objects: - // - Any object or value whose internal [[Class]] property is not "[object Object]" - // - DOM nodes - // - window - if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } - - if ( obj.constructor && - !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { - return false; - } - - // If the function hasn't returned already, we're confident that - // |obj| is a plain object, created by {} or constructed with new Object - return true; - }, - - isEmptyObject: function( obj ) { - var name; - for ( name in obj ) { - return false; - } - return true; - }, - - type: function( obj ) { - if ( obj == null ) { - return obj + ""; - } - // Support: Android < 4.0, iOS < 6 (functionish RegExp) - return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call(obj) ] || "object" : - typeof obj; - }, - - // Evaluates a script in a global context - globalEval: function( code ) { - var script, - indirect = eval; - - code = jQuery.trim( code ); - - if ( code ) { - // If the code includes a valid, prologue position - // strict mode pragma, execute code by injecting a - // script tag into the document. - if ( code.indexOf("use strict") === 1 ) { - script = document.createElement("script"); - script.text = code; - document.head.appendChild( script ).parentNode.removeChild( script ); - } else { - // Otherwise, avoid the DOM node creation, insertion - // and removal by using an indirect global eval - indirect( code ); - } - } - }, - - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - }, - - // args is for internal usage only - each: function( obj, callback, args ) { - var value, - i = 0, - length = obj.length, - isArray = isArraylike( obj ); - - if ( args ) { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } - } - } - } - - return obj; - }, - - // Support: Android<4.1 - trim: function( text ) { - return text == null ? - "" : - ( text + "" ).replace( rtrim, "" ); - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; - - if ( arr != null ) { - if ( isArraylike( Object(arr) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - push.call( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - return arr == null ? -1 : indexOf.call( arr, elem, i ); - }, - - merge: function( first, second ) { - var len = +second.length, - j = 0, - i = first.length; - - for ( ; j < len; j++ ) { - first[ i++ ] = second[ j ]; - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, invert ) { - var callbackInverse, - matches = [], - i = 0, - length = elems.length, - callbackExpect = !invert; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - callbackInverse = !callback( elems[ i ], i ); - if ( callbackInverse !== callbackExpect ) { - matches.push( elems[ i ] ); - } - } - - return matches; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, - i = 0, - length = elems.length, - isArray = isArraylike( elems ), - ret = []; - - // Go through the array, translating each of the items to their new values - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - } - - // Flatten any nested arrays - return concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - var tmp, args, proxy; - - if ( typeof context === "string" ) { - tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - args = slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || jQuery.guid++; - - return proxy; - }, - - now: Date.now, - - // jQuery.support is not used in Core but other projects attach their - // properties to it so it needs to exist. - support: support -}); - -// Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); - -function isArraylike( obj ) { - var length = obj.length, - type = jQuery.type( obj ); - - if ( type === "function" || jQuery.isWindow( obj ) ) { - return false; - } - - if ( obj.nodeType === 1 && length ) { - return true; - } - - return type === "array" || length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj; -} -var Sizzle = -/*! - * Sizzle CSS Selector Engine v1.10.19 - * http://sizzlejs.com/ - * - * Copyright 2013 jQuery Foundation, Inc. and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2014-04-18 - */ -(function( window ) { - -var i, - support, - Expr, - getText, - isXML, - tokenize, - compile, - select, - outermostContext, - sortInput, - hasDuplicate, - - // Local document vars - setDocument, - document, - docElem, - documentIsHTML, - rbuggyQSA, - rbuggyMatches, - matches, - contains, - - // Instance-specific data - expando = "sizzle" + -(new Date()), - preferredDoc = window.document, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - } - return 0; - }, - - // General-purpose constants - strundefined = typeof undefined, - MAX_NEGATIVE = 1 << 31, - - // Instance methods - hasOwn = ({}).hasOwnProperty, - arr = [], - pop = arr.pop, - push_native = arr.push, - push = arr.push, - slice = arr.slice, - // Use a stripped-down indexOf if we can't use a native one - indexOf = arr.indexOf || function( elem ) { - var i = 0, - len = this.length; - for ( ; i < len; i++ ) { - if ( this[i] === elem ) { - return i; - } - } - return -1; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", - - // Regular expressions - - // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - // http://www.w3.org/TR/css3-syntax/#characters - characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", - - // Loosely modeled on CSS identifier characters - // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors - // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = characterEncoding.replace( "w", "w#" ), - - // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + - // Operator (capture 2) - "*([*^$|!~]?=)" + whitespace + - // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" - "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + - "*\\]", - - pseudos = ":(" + characterEncoding + ")(?:\\((" + - // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: - // 1. quoted (capture 3; capture 4 or capture 5) - "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + - // 2. simple (capture 6) - "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + - // 3. anything else (capture 2) - ".*" + - ")\\)|)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), - - rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), - - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), - - matchExpr = { - "ID": new RegExp( "^#(" + characterEncoding + ")" ), - "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), - // For use in libraries implementing .is() - // We use this for POS matching in `select` - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + - whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, - - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - rnative = /^[^{]+\{\s*\[native \w/, - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rsibling = /[+~]/, - rescape = /'|\\/g, - - // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), - funescape = function( _, escaped, escapedWhitespace ) { - var high = "0x" + escaped - 0x10000; - // NaN means non-codepoint - // Support: Firefox<24 - // Workaround erroneous numeric interpretation of +"0x" - return high !== high || escapedWhitespace ? - escaped : - high < 0 ? - // BMP codepoint - String.fromCharCode( high + 0x10000 ) : - // Supplemental Plane codepoint (surrogate pair) - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }; - -// Optimize for push.apply( _, NodeList ) -try { - push.apply( - (arr = slice.call( preferredDoc.childNodes )), - preferredDoc.childNodes - ); - // Support: Android<4.0 - // Detect silently failing push.apply - arr[ preferredDoc.childNodes.length ].nodeType; -} catch ( e ) { - push = { apply: arr.length ? - - // Leverage slice if possible - function( target, els ) { - push_native.apply( target, slice.call(els) ); - } : - - // Support: IE<9 - // Otherwise append directly - function( target, els ) { - var j = target.length, - i = 0; - // Can't trust NodeList.length - while ( (target[j++] = els[i++]) ) {} - target.length = j - 1; - } - }; -} - -function Sizzle( selector, context, results, seed ) { - var match, elem, m, nodeType, - // QSA vars - i, groups, old, nid, newContext, newSelector; - - if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { - setDocument( context ); - } - - context = context || document; - results = results || []; - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { - return []; - } - - if ( documentIsHTML && !seed ) { - - // Shortcuts - if ( (match = rquickExpr.exec( selector )) ) { - // Speed-up: Sizzle("#ID") - if ( (m = match[1]) ) { - if ( nodeType === 9 ) { - elem = context.getElementById( m ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document (jQuery #6963) - if ( elem && elem.parentNode ) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - } else { - // Context is not a document - if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && - contains( context, elem ) && elem.id === m ) { - results.push( elem ); - return results; - } - } - - // Speed-up: Sizzle("TAG") - } else if ( match[2] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // QSA path - if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - nid = old = expando; - newContext = context; - newSelector = nodeType === 9 && selector; - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - groups = tokenize( selector ); - - if ( (old = context.getAttribute("id")) ) { - nid = old.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - nid = "[id='" + nid + "'] "; - - i = groups.length; - while ( i-- ) { - groups[i] = nid + toSelector( groups[i] ); - } - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; - newSelector = groups.join(","); - } - - if ( newSelector ) { - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); - } - } - } - } - } - - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); -} - -/** - * Create key-value caches of limited size - * @returns {Function(string, Object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ -function createCache() { - var keys = []; - - function cache( key, value ) { - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if ( keys.push( key + " " ) > Expr.cacheLength ) { - // Only keep the most recent entries - delete cache[ keys.shift() ]; - } - return (cache[ key + " " ] = value); - } - return cache; -} - -/** - * Mark a function for special use by Sizzle - * @param {Function} fn The function to mark - */ -function markFunction( fn ) { - fn[ expando ] = true; - return fn; -} - -/** - * Support testing using an element - * @param {Function} fn Passed the created div and expects a boolean result - */ -function assert( fn ) { - var div = document.createElement("div"); - - try { - return !!fn( div ); - } catch (e) { - return false; - } finally { - // Remove from its parent by default - if ( div.parentNode ) { - div.parentNode.removeChild( div ); - } - // release memory in IE - div = null; - } -} - -/** - * Adds the same handler for all of the specified attrs - * @param {String} attrs Pipe-separated list of attributes - * @param {Function} handler The method that will be applied - */ -function addHandle( attrs, handler ) { - var arr = attrs.split("|"), - i = attrs.length; - - while ( i-- ) { - Expr.attrHandle[ arr[i] ] = handler; - } -} - -/** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b - */ -function siblingCheck( a, b ) { - var cur = b && a, - diff = cur && a.nodeType === 1 && b.nodeType === 1 && - ( ~b.sourceIndex || MAX_NEGATIVE ) - - ( ~a.sourceIndex || MAX_NEGATIVE ); - - // Use IE sourceIndex if available on both nodes - if ( diff ) { - return diff; - } - - // Check if b follows a - if ( cur ) { - while ( (cur = cur.nextSibling) ) { - if ( cur === b ) { - return -1; - } - } - } - - return a ? 1 : -1; -} - -/** - * Returns a function to use in pseudos for input types - * @param {String} type - */ -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ -function createPositionalPseudo( fn ) { - return markFunction(function( argument ) { - argument = +argument; - return markFunction(function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ (j = matchIndexes[i]) ] ) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); -} - -/** - * Checks a node for validity as a Sizzle context - * @param {Element|Object=} context - * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value - */ -function testContext( context ) { - return context && typeof context.getElementsByTagName !== strundefined && context; -} - -// Expose support vars for convenience -support = Sizzle.support = {}; - -/** - * Detects XML nodes - * @param {Element|Object} elem An element or a document - * @returns {Boolean} True iff elem is a non-HTML XML node - */ -isXML = Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -/** - * Sets document-related variables once based on the current document - * @param {Element|Object} [doc] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ -setDocument = Sizzle.setDocument = function( node ) { - var hasCompare, - doc = node ? node.ownerDocument || node : preferredDoc, - parent = doc.defaultView; - - // If no document and documentElement is available, return - if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } - - // Set our document - document = doc; - docElem = doc.documentElement; - - // Support tests - documentIsHTML = !isXML( doc ); - - // Support: IE>8 - // If iframe document is assigned to "document" variable and if iframe has been reloaded, - // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 - // IE6-8 do not support the defaultView property so parent will be undefined - if ( parent && parent !== parent.top ) { - // IE11 does not have attachEvent, so all must suffer - if ( parent.addEventListener ) { - parent.addEventListener( "unload", function() { - setDocument(); - }, false ); - } else if ( parent.attachEvent ) { - parent.attachEvent( "onunload", function() { - setDocument(); - }); - } - } - - /* Attributes - ---------------------------------------------------------------------- */ - - // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) - support.attributes = assert(function( div ) { - div.className = "i"; - return !div.getAttribute("className"); - }); - - /* getElement(s)By* - ---------------------------------------------------------------------- */ - - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert(function( div ) { - div.appendChild( doc.createComment("") ); - return !div.getElementsByTagName("*").length; - }); - - // Check if getElementsByClassName can be trusted - support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { - div.innerHTML = "
    "; - - // Support: Safari<4 - // Catch class over-caching - div.firstChild.className = "i"; - // Support: Opera<10 - // Catch gEBCN failure to find non-leading classes - return div.getElementsByClassName("i").length === 2; - }); - - // Support: IE<10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert(function( div ) { - docElem.appendChild( div ).id = expando; - return !doc.getElementsByName || !doc.getElementsByName( expando ).length; - }); - - // ID find and filter - if ( support.getById ) { - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== strundefined && documentIsHTML ) { - var m = context.getElementById( id ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [ m ] : []; - } - }; - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute("id") === attrId; - }; - }; - } else { - // Support: IE6/7 - // getElementById is not reliable as a find shortcut - delete Expr.find["ID"]; - - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); - return node && node.value === attrId; - }; - }; - } - - // Tag - Expr.find["TAG"] = support.getElementsByTagName ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== strundefined ) { - return context.getElementsByTagName( tag ); - } - } : - function( tag, context ) { - var elem, - tmp = [], - i = 0, - results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - while ( (elem = results[i++]) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }; - - // Class - Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - // QSA and matchesSelector support - - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; - - // qSa(:focus) reports false when true (Chrome 21) - // We allow this because of a bug in IE8/9 that throws an error - // whenever `document.activeElement` is accessed on an iframe - // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See http://bugs.jquery.com/ticket/13378 - rbuggyQSA = []; - - if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( div ) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explicitly - // setting a boolean content attribute, - // since its presence should be enough - // http://bugs.jquery.com/ticket/12359 - div.innerHTML = ""; - - // Support: IE8, Opera 11-12.16 - // Nothing should be selected when empty strings follow ^= or $= or *= - // The test attribute must be unknown in Opera but "safe" for WinRT - // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if ( div.querySelectorAll("[msallowclip^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); - } - - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if ( !div.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - }); - - assert(function( div ) { - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - var input = doc.createElement("input"); - input.setAttribute( "type", "hidden" ); - div.appendChild( input ).setAttribute( "name", "D" ); - - // Support: IE8 - // Enforce case-sensitivity of name attribute - if ( div.querySelectorAll("[name=d]").length ) { - rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":enabled").length ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Opera 10-11 does not throw on post-comma invalid pseudos - div.querySelectorAll("*,:x"); - rbuggyQSA.push(",.*:"); - }); - } - - if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || - docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector) )) ) { - - assert(function( div ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( div, "div" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( div, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - }); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); - - /* Contains - ---------------------------------------------------------------------- */ - hasCompare = rnative.test( docElem.compareDocumentPosition ); - - // Element contains another - // Purposefully does not implement inclusive descendent - // As in, an element does not contain itself - contains = hasCompare || rnative.test( docElem.contains ) ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - )); - } : - function( a, b ) { - if ( b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - } - return false; - }; - - /* Sorting - ---------------------------------------------------------------------- */ - - // Document order sorting - sortOrder = hasCompare ? - function( a, b ) { - - // Flag for duplicate removal - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - // Sort on method existence if only one input has compareDocumentPosition - var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if ( compare ) { - return compare; - } - - // Calculate position if both inputs belong to the same document - compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? - a.compareDocumentPosition( b ) : - - // Otherwise we know they are disconnected - 1; - - // Disconnected nodes - if ( compare & 1 || - (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { - - // Choose the first element that is related to our preferred document - if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { - return -1; - } - if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { - return 1; - } - - // Maintain original order - return sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; - } : - function( a, b ) { - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [ a ], - bp = [ b ]; - - // Parentless nodes are either documents or disconnected - if ( !aup || !bup ) { - return a === doc ? -1 : - b === doc ? 1 : - aup ? -1 : - bup ? 1 : - sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; - - // If the nodes are siblings, we can do a quick check - } else if ( aup === bup ) { - return siblingCheck( a, b ); - } - - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ( (cur = cur.parentNode) ) { - ap.unshift( cur ); - } - cur = b; - while ( (cur = cur.parentNode) ) { - bp.unshift( cur ); - } - - // Walk down the tree looking for a discrepancy - while ( ap[i] === bp[i] ) { - i++; - } - - return i ? - // Do a sibling check if the nodes have a common ancestor - siblingCheck( ap[i], bp[i] ) : - - // Otherwise nodes in our document sort first - ap[i] === preferredDoc ? -1 : - bp[i] === preferredDoc ? 1 : - 0; - }; - - return doc; -}; - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); - - if ( support.matchesSelector && documentIsHTML && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || support.disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch(e) {} - } - - return Sizzle( expr, document, null, [ elem ] ).length > 0; -}; - -Sizzle.contains = function( context, elem ) { - // Set document vars if needed - if ( ( context.ownerDocument || context ) !== document ) { - setDocument( context ); - } - return contains( context, elem ); -}; - -Sizzle.attr = function( elem, name ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - var fn = Expr.attrHandle[ name.toLowerCase() ], - // Don't get fooled by Object.prototype properties (jQuery #13807) - val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? - fn( elem, name, !documentIsHTML ) : - undefined; - - return val !== undefined ? - val : - support.attributes || !documentIsHTML ? - elem.getAttribute( name ) : - (val = elem.getAttributeNode(name)) && val.specified ? - val.value : - null; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ -Sizzle.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); - - if ( hasDuplicate ) { - while ( (elem = results[i++]) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } - - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; - - return results; -}; - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - // If no nodeType, this is expected to be an array - while ( (node = elem[i++]) ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (jQuery #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - - return ret; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( runescape, funescape ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1].slice( 0, 3 ) === "nth" ) { - // nth-* requires argument - if ( !match[3] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); - match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); - - // other types prohibit arguments - } else if ( match[3] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var excess, - unquoted = !match[6] && match[2]; - - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - // Accept quoted arguments as-is - if ( match[3] ) { - match[2] = match[4] || match[5] || ""; - - // Strip excess characters from unquoted arguments - } else if ( unquoted && rpseudo.test( unquoted ) && - // Get excess from tokenize (recursively) - (excess = tokenize( unquoted, true )) && - // advance to the next closing parenthesis - (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - - // excess is a negative index - match[0] = match[0].slice( 0, excess ); - match[2] = unquoted.slice( 0, excess ); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { return true; } : - function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && - classCache( className, function( elem ) { - return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); - }); - }, - - "ATTR": function( name, operator, check ) { - return function( elem ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : - false; - }; - }, - - "CHILD": function( type, what, argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, context, xml ) { - var cache, outerCache, node, diff, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType; - - if ( parent ) { - - // :(first|last|only)-(child|of-type) - if ( simple ) { - while ( dir ) { - node = elem; - while ( (node = node[ dir ]) ) { - if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { - return false; - } - } - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - // non-xml :nth-child(...) stores cache data on `parent` - if ( forward && useCache ) { - // Seek `elem` from a previously-cached index - outerCache = parent[ expando ] || (parent[ expando ] = {}); - cache = outerCache[ type ] || []; - nodeIndex = cache[0] === dirruns && cache[1]; - diff = cache[0] === dirruns && cache[2]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( (node = ++nodeIndex && node && node[ dir ] || - - // Fallback to seeking `elem` from the start - (diff = nodeIndex = 0) || start.pop()) ) { - - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - outerCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - // Use previously-cached element index if available - } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { - diff = cache[1]; - - // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) - } else { - // Use the same loop as above to seek `elem` from the start - while ( (node = ++nodeIndex && node && node[ dir ] || - (diff = nodeIndex = 0) || start.pop()) ) { - - if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { - // Cache the index of each encountered element - if ( useCache ) { - (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction(function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf.call( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); - } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - // Potentially complex pseudos - "not": markFunction(function( selector ) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction(function( seed, matches, context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( (elem = unmatched[i]) ) { - seed[i] = !(matches[i] = elem); - } - } - }) : - function( elem, context, xml ) { - input[0] = elem; - matcher( input, null, xml, results ); - return !results.pop(); - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "contains": markFunction(function( text ) { - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - "lang": markFunction( function( lang ) { - // lang value must be a valid identifier - if ( !ridentifier.test(lang || "") ) { - Sizzle.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( (elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); - return false; - }; - }), - - // Miscellaneous - "target": function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - "root": function( elem ) { - return elem === docElem; - }, - - "focus": function( elem ) { - return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); - }, - - // Boolean properties - "enabled": function( elem ) { - return elem.disabled === false; - }, - - "disabled": function( elem ) { - return elem.disabled === true; - }, - - "checked": function( elem ) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), - // but not by others (comment: 8; processing instruction: 7; etc.) - // nodeType < 6 works because attributes (2) do not appear as children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeType < 6 ) { - return false; - } - } - return true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - // Element/input types - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "text": function( elem ) { - var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && - - // Support: IE<8 - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); - }, - - // Position-in-collection - "first": createPositionalPseudo(function() { - return [ 0 ]; - }), - - "last": createPositionalPseudo(function( matchIndexes, length ) { - return [ length - 1 ]; - }), - - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), - - "even": createPositionalPseudo(function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } -}; - -Expr.pseudos["nth"] = Expr.pseudos["eq"]; - -// Add button/input type pseudos -for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); -} -for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); -} - -// Easy API for creating new setFilters -function setFilters() {} -setFilters.prototype = Expr.filters = Expr.pseudos; -Expr.setFilters = new setFilters(); - -tokenize = Sizzle.tokenize = function( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || (match = rcomma.exec( soFar )) ) { - if ( match ) { - // Don't consume trailing commas as valid - soFar = soFar.slice( match[0].length ) || soFar; - } - groups.push( (tokens = []) ); - } - - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - matched = match.shift(); - tokens.push({ - value: matched, - // Cast descendant combinators to space - type: match[0].replace( rtrim, " " ) - }); - soFar = soFar.slice( matched.length ); - } - - // Filters - for ( type in Expr.filter ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - (match = preFilters[ type ]( match ))) ) { - matched = match.shift(); - tokens.push({ - value: matched, - type: type, - matches: match - }); - soFar = soFar.slice( matched.length ); - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -}; - -function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[i].value; - } - return selector; -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - checkNonElements = base && dir === "parentNode", - doneName = done++; - - return combinator.first ? - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - var oldCache, outerCache, - newCache = [ dirruns, doneName ]; - - // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching - if ( xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || (elem[ expando ] = {}); - if ( (oldCache = outerCache[ dir ]) && - oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { - - // Assign to newCache so results back-propagate to previous elements - return (newCache[ 2 ] = oldCache[ 2 ]); - } else { - // Reuse newcache so results back-propagate to previous elements - outerCache[ dir ] = newCache; - - // A match means we're done; a fail means we have to keep checking - if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { - return true; - } - } - } - } - } - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[i]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[0]; -} - -function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results ); - } - return results; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( (elem = unmatched[i]) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction(function( seed, results, context, xml ) { - var temp, i, elem, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, - - matcherOut = matcher ? - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results : - matcherIn; - - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } - - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while ( i-- ) { - if ( (elem = temp[i]) ) { - matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); - } - } - } - - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) ) { - // Restore matcherIn since elem is not yet a final match - temp.push( (matcherIn[i] = elem) ); - } - } - postFinder( null, (matcherOut = []), temp, xml ); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) && - (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { - - seed[temp] = !(results[temp] = elem); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - }); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[0].type ], - implicitRelative = leadingRelative || Expr.relative[" "], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf.call( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - } ]; - - for ( ; i < len; i++ ) { - if ( (matcher = Expr.relative[ tokens[i].type ]) ) { - matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; - } else { - matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[j].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) - ).replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, outermost ) { - var elem, j, matcher, - matchedCount = 0, - i = "0", - unmatched = seed && [], - setMatched = [], - contextBackup = outermostContext, - // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), - len = elems.length; - - if ( outermost ) { - outermostContext = context !== document && context; - } - - // Add elements passing elementMatchers directly to results - // Keep `i` a string if there are no elements so `matchedCount` will be "00" below - // Support: IE<9, Safari - // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id - for ( ; i !== len && (elem = elems[i]) != null; i++ ) { - if ( byElement && elem ) { - j = 0; - while ( (matcher = elementMatchers[j++]) ) { - if ( matcher( elem, context, xml ) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - } - } - - // Track unmatched elements for set filters - if ( bySet ) { - // They will have gone through all possible matchers - if ( (elem = !matcher && elem) ) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } - - // Apply set filters to unmatched elements - matchedCount += i; - if ( bySet && i !== matchedCount ) { - j = 0; - while ( (matcher = setMatchers[j++]) ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !(unmatched[i] || setMatched[i]) ) { - setMatched[i] = pop.call( results ); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } - - // Add matches to results - push.apply( results, setMatched ); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - Sizzle.uniqueSort( results ); - } - } - - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; - - if ( !cached ) { - // Generate a function of recursive functions that can be used to check each element - if ( !match ) { - match = tokenize( selector ); - } - i = match.length; - while ( i-- ) { - cached = matcherFromTokens( match[i] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - - // Save selector and tokenization - cached.selector = selector; - } - return cached; -}; - -/** - * A low-level selection function that works with Sizzle's compiled - * selector functions - * @param {String|Function} selector A selector or a pre-compiled - * selector function built with Sizzle.compile - * @param {Element} context - * @param {Array} [results] - * @param {Array} [seed] A set of elements to match against - */ -select = Sizzle.select = function( selector, context, results, seed ) { - var i, tokens, token, type, find, - compiled = typeof selector === "function" && selector, - match = !seed && tokenize( (selector = compiled.selector || selector) ); - - results = results || []; - - // Try to minimize operations if there is no seed and only one group - if ( match.length === 1 ) { - - // Take a shortcut and set the context if the root selector is an ID - tokens = match[0] = match[0].slice( 0 ); - if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - support.getById && context.nodeType === 9 && documentIsHTML && - Expr.relative[ tokens[1].type ] ) { - - context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; - if ( !context ) { - return results; - - // Precompiled matchers will still verify ancestry, so step up a level - } else if ( compiled ) { - context = context.parentNode; - } - - selector = selector.slice( tokens.shift().value.length ); - } - - // Fetch a seed set for right-to-left matching - i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[i]; - - // Abort if we hit a combinator - if ( Expr.relative[ (type = token.type) ] ) { - break; - } - if ( (find = Expr.find[ type ]) ) { - // Search, expanding context for leading sibling combinators - if ( (seed = find( - token.matches[0].replace( runescape, funescape ), - rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context - )) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } - - break; - } - } - } - } - - // Compile and execute a filtering function if one is not provided - // Provide `match` to avoid retokenization if we modified the selector above - ( compiled || compile( selector, match ) )( - seed, - context, - !documentIsHTML, - results, - rsibling.test( selector ) && testContext( context.parentNode ) || context - ); - return results; -}; - -// One-time assignments - -// Sort stability -support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; - -// Support: Chrome<14 -// Always assume duplicates if they aren't passed to the comparison function -support.detectDuplicates = !!hasDuplicate; - -// Initialize against the default document -setDocument(); - -// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) -// Detached nodes confoundingly follow *each other* -support.sortDetached = assert(function( div1 ) { - // Should return 1, but returns 4 (following) - return div1.compareDocumentPosition( document.createElement("div") ) & 1; -}); - -// Support: IE<8 -// Prevent attribute/property "interpolation" -// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx -if ( !assert(function( div ) { - div.innerHTML = ""; - return div.firstChild.getAttribute("href") === "#" ; -}) ) { - addHandle( "type|href|height|width", function( elem, name, isXML ) { - if ( !isXML ) { - return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); - } - }); -} - -// Support: IE<9 -// Use defaultValue in place of getAttribute("value") -if ( !support.attributes || !assert(function( div ) { - div.innerHTML = ""; - div.firstChild.setAttribute( "value", "" ); - return div.firstChild.getAttribute( "value" ) === ""; -}) ) { - addHandle( "value", function( elem, name, isXML ) { - if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { - return elem.defaultValue; - } - }); -} - -// Support: IE<9 -// Use getAttributeNode to fetch booleans when getAttribute lies -if ( !assert(function( div ) { - return div.getAttribute("disabled") == null; -}) ) { - addHandle( booleans, function( elem, name, isXML ) { - var val; - if ( !isXML ) { - return elem[ name ] === true ? name.toLowerCase() : - (val = elem.getAttributeNode( name )) && val.specified ? - val.value : - null; - } - }); -} - -return Sizzle; - -})( window ); - - - -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.pseudos; -jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; - - - -var rneedsContext = jQuery.expr.match.needsContext; - -var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); - - - -var risSimple = /^.[^:#\[\.,]*$/; - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, not ) { - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep( elements, function( elem, i ) { - /* jshint -W018 */ - return !!qualifier.call( elem, i, elem ) !== not; - }); - - } - - if ( qualifier.nodeType ) { - return jQuery.grep( elements, function( elem ) { - return ( elem === qualifier ) !== not; - }); - - } - - if ( typeof qualifier === "string" ) { - if ( risSimple.test( qualifier ) ) { - return jQuery.filter( qualifier, elements, not ); - } - - qualifier = jQuery.filter( qualifier, elements ); - } - - return jQuery.grep( elements, function( elem ) { - return ( indexOf.call( qualifier, elem ) >= 0 ) !== not; - }); -} - -jQuery.filter = function( expr, elems, not ) { - var elem = elems[ 0 ]; - - if ( not ) { - expr = ":not(" + expr + ")"; - } - - return elems.length === 1 && elem.nodeType === 1 ? - jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : - jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { - return elem.nodeType === 1; - })); -}; - -jQuery.fn.extend({ - find: function( selector ) { - var i, - len = this.length, - ret = [], - self = this; - - if ( typeof selector !== "string" ) { - return this.pushStack( jQuery( selector ).filter(function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - }) ); - } - - for ( i = 0; i < len; i++ ) { - jQuery.find( selector, self[ i ], ret ); - } - - // Needed because $( selector, context ) becomes $( context ).find( selector ) - ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); - ret.selector = this.selector ? this.selector + " " + selector : selector; - return ret; - }, - filter: function( selector ) { - return this.pushStack( winnow(this, selector || [], false) ); - }, - not: function( selector ) { - return this.pushStack( winnow(this, selector || [], true) ); - }, - is: function( selector ) { - return !!winnow( - this, - - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - typeof selector === "string" && rneedsContext.test( selector ) ? - jQuery( selector ) : - selector || [], - false - ).length; - } -}); - - -// Initialize a jQuery object - - -// A central reference to the root jQuery(document) -var rootjQuery, - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - // Strict HTML recognition (#11290: must start with <) - rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, - - init = jQuery.fn.init = function( selector, context ) { - var match, elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; - - // scripts is true for back-compat - // Intentionally let the error be thrown if parseHTML is not present - jQuery.merge( this, jQuery.parseHTML( - match[1], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); - - // HANDLE: $(html, props) - if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - // Properties of context are called as methods if possible - if ( jQuery.isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); - - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } - - return this; - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[2] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return typeof rootjQuery.ready !== "undefined" ? - rootjQuery.ready( selector ) : - // Execute immediately if ready is not present - selector( jQuery ); - } - - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray( selector, this ); - }; - -// Give the init function the jQuery prototype for later instantiation -init.prototype = jQuery.fn; - -// Initialize central reference -rootjQuery = jQuery( document ); - - -var rparentsprev = /^(?:parents|prev(?:Until|All))/, - // methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.extend({ - dir: function( elem, dir, until ) { - var matched = [], - truncate = until !== undefined; - - while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) { - if ( elem.nodeType === 1 ) { - if ( truncate && jQuery( elem ).is( until ) ) { - break; - } - matched.push( elem ); - } - } - return matched; - }, - - sibling: function( n, elem ) { - var matched = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - matched.push( n ); - } - } - - return matched; - } -}); - -jQuery.fn.extend({ - has: function( target ) { - var targets = jQuery( target, this ), - l = targets.length; - - return this.filter(function() { - var i = 0; - for ( ; i < l; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - matched = [], - pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? - jQuery( selectors, context || this.context ) : - 0; - - for ( ; i < l; i++ ) { - for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { - // Always skip document fragments - if ( cur.nodeType < 11 && (pos ? - pos.index(cur) > -1 : - - // Don't pass non-elements to Sizzle - cur.nodeType === 1 && - jQuery.find.matchesSelector(cur, selectors)) ) { - - matched.push( cur ); - break; - } - } - } - - return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; - } - - // index in selector - if ( typeof elem === "string" ) { - return indexOf.call( jQuery( elem ), this[ 0 ] ); - } - - // Locate the position of the desired element - return indexOf.call( this, - - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[ 0 ] : elem - ); - }, - - add: function( selector, context ) { - return this.pushStack( - jQuery.unique( - jQuery.merge( this.get(), jQuery( selector, context ) ) - ) - ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter(selector) - ); - } -}); - -function sibling( cur, dir ) { - while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {} - return cur; -} - -jQuery.each({ - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return elem.contentDocument || jQuery.merge( [], elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var matched = jQuery.map( this, fn, until ); - - if ( name.slice( -5 ) !== "Until" ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - matched = jQuery.filter( selector, matched ); - } - - if ( this.length > 1 ) { - // Remove duplicates - if ( !guaranteedUnique[ name ] ) { - jQuery.unique( matched ); - } - - // Reverse order for parents* and prev-derivatives - if ( rparentsprev.test( name ) ) { - matched.reverse(); - } - } - - return this.pushStack( matched ); - }; -}); -var rnotwhite = (/\S+/g); - - - -// String to Object options format cache -var optionsCache = {}; - -// Convert String-formatted options into Object-formatted ones and store in cache -function createOptions( options ) { - var object = optionsCache[ options ] = {}; - jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { - object[ flag ] = true; - }); - return object; -} - -/* - * Create a callback list using the following parameters: - * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible options: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -jQuery.Callbacks = function( options ) { - - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - ( optionsCache[ options ] || createOptions( options ) ) : - jQuery.extend( {}, options ); - - var // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list was already fired - fired, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // Actual callback list - list = [], - // Stack of fire calls for repeatable lists - stack = !options.once && [], - // Fire callbacks - fire = function( data ) { - memory = options.memory && data; - fired = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - firing = true; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { - memory = false; // To prevent further calls using add - break; - } - } - firing = false; - if ( list ) { - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } - } else if ( memory ) { - list = []; - } else { - self.disable(); - } - } - }, - // Actual Callbacks object - self = { - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - // First, we save the current length - var start = list.length; - (function add( args ) { - jQuery.each( args, function( _, arg ) { - var type = jQuery.type( arg ); - if ( type === "function" ) { - if ( !options.unique || !self.has( arg ) ) { - list.push( arg ); - } - } else if ( arg && arg.length && type !== "string" ) { - // Inspect recursively - add( arg ); - } - }); - })( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away - } else if ( memory ) { - firingStart = start; - fire( memory ); - } - } - return this; - }, - // Remove a callback from the list - remove: function() { - if ( list ) { - jQuery.each( arguments, function( _, arg ) { - var index; - while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - // Handle firing indexes - if ( firing ) { - if ( index <= firingLength ) { - firingLength--; - } - if ( index <= firingIndex ) { - firingIndex--; - } - } - } - }); - } - return this; - }, - // Check if a given callback is in the list. - // If no argument is given, return whether or not list has callbacks attached. - has: function( fn ) { - return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); - }, - // Remove all callbacks from the list - empty: function() { - list = []; - firingLength = 0; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory ) { - self.disable(); - } - return this; - }, - // Is it locked? - locked: function() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( list && ( !fired || stack ) ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - if ( firing ) { - stack.push( args ); - } else { - fire( args ); - } - } - return this; - }, - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; -}; - - -jQuery.extend({ - - Deferred: function( func ) { - var tuples = [ - // action, add listener, listener list, final state - [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], - [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], - [ "notify", "progress", jQuery.Callbacks("memory") ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - then: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - return jQuery.Deferred(function( newDefer ) { - jQuery.each( tuples, function( i, tuple ) { - var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; - // deferred[ done | fail | progress ] for forwarding actions to newDefer - deferred[ tuple[1] ](function() { - var returned = fn && fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise() - .done( newDefer.resolve ) - .fail( newDefer.reject ) - .progress( newDefer.notify ); - } else { - newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); - } - }); - }); - fns = null; - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return obj != null ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; - - // Keep pipe for back-compat - promise.pipe = promise.then; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 3 ]; - - // promise[ done | fail | progress ] = list.add - promise[ tuple[1] ] = list.add; - - // Handle state - if ( stateString ) { - list.add(function() { - // state = [ resolved | rejected ] - state = stateString; - - // [ reject_list | resolve_list ].disable; progress_list.lock - }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); - } - - // deferred[ resolve | reject | notify ] - deferred[ tuple[0] ] = function() { - deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); - return this; - }; - deferred[ tuple[0] + "With" ] = list.fireWith; - }); - - // Make the deferred a promise - promise.promise( deferred ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( subordinate /* , ..., subordinateN */ ) { - var i = 0, - resolveValues = slice.call( arguments ), - length = resolveValues.length, - - // the count of uncompleted subordinates - remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, - - // the master Deferred. If resolveValues consist of only a single Deferred, just use that. - deferred = remaining === 1 ? subordinate : jQuery.Deferred(), - - // Update function for both resolve and progress values - updateFunc = function( i, contexts, values ) { - return function( value ) { - contexts[ i ] = this; - values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; - if ( values === progressValues ) { - deferred.notifyWith( contexts, values ); - } else if ( !( --remaining ) ) { - deferred.resolveWith( contexts, values ); - } - }; - }, - - progressValues, progressContexts, resolveContexts; - - // add listeners to Deferred subordinates; treat others as resolved - if ( length > 1 ) { - progressValues = new Array( length ); - progressContexts = new Array( length ); - resolveContexts = new Array( length ); - for ( ; i < length; i++ ) { - if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { - resolveValues[ i ].promise() - .done( updateFunc( i, resolveContexts, resolveValues ) ) - .fail( deferred.reject ) - .progress( updateFunc( i, progressContexts, progressValues ) ); - } else { - --remaining; - } - } - } - - // if we're not waiting on anything, resolve the master - if ( !remaining ) { - deferred.resolveWith( resolveContexts, resolveValues ); - } - - return deferred.promise(); - } -}); - - -// The deferred used on DOM ready -var readyList; - -jQuery.fn.ready = function( fn ) { - // Add the callback - jQuery.ready.promise().done( fn ); - - return this; -}; - -jQuery.extend({ - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - - // Trigger any bound ready events - if ( jQuery.fn.triggerHandler ) { - jQuery( document ).triggerHandler( "ready" ); - jQuery( document ).off( "ready" ); - } - } -}); - -/** - * The ready event handler and self cleanup method - */ -function completed() { - document.removeEventListener( "DOMContentLoaded", completed, false ); - window.removeEventListener( "load", completed, false ); - jQuery.ready(); -} - -jQuery.ready.promise = function( obj ) { - if ( !readyList ) { - - readyList = jQuery.Deferred(); - - // Catch cases where $(document).ready() is called after the browser event has already occurred. - // we once tried to use readyState "interactive" here, but it caused issues like the one - // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout( jQuery.ready ); - - } else { - - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed, false ); - } - } - return readyList.promise( obj ); -}; - -// Kick off the DOM ready check even if the user does not -jQuery.ready.promise(); - - - - -// Multifunctional method to get and set values of a collection -// The value/s can optionally be executed if it's a function -var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - len = elems.length, - bulk = key == null; - - // Sets many values - if ( jQuery.type( key ) === "object" ) { - chainable = true; - for ( i in key ) { - jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); - } - - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !jQuery.isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < len; i++ ) { - fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); - } - } - } - - return chainable ? - elems : - - // Gets - bulk ? - fn.call( elems ) : - len ? fn( elems[0], key ) : emptyGet; -}; - - -/** - * Determines whether an object can have data - */ -jQuery.acceptData = function( owner ) { - // Accepts only: - // - Node - // - Node.ELEMENT_NODE - // - Node.DOCUMENT_NODE - // - Object - // - Any - /* jshint -W018 */ - return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); -}; - - -function Data() { - // Support: Android < 4, - // Old WebKit does not have Object.preventExtensions/freeze method, - // return new empty object instead with no [[set]] accessor - Object.defineProperty( this.cache = {}, 0, { - get: function() { - return {}; - } - }); - - this.expando = jQuery.expando + Math.random(); -} - -Data.uid = 1; -Data.accepts = jQuery.acceptData; - -Data.prototype = { - key: function( owner ) { - // We can accept data for non-element nodes in modern browsers, - // but we should not, see #8335. - // Always return the key for a frozen object. - if ( !Data.accepts( owner ) ) { - return 0; - } - - var descriptor = {}, - // Check if the owner object already has a cache key - unlock = owner[ this.expando ]; - - // If not, create one - if ( !unlock ) { - unlock = Data.uid++; - - // Secure it in a non-enumerable, non-writable property - try { - descriptor[ this.expando ] = { value: unlock }; - Object.defineProperties( owner, descriptor ); - - // Support: Android < 4 - // Fallback to a less secure definition - } catch ( e ) { - descriptor[ this.expando ] = unlock; - jQuery.extend( owner, descriptor ); - } - } - - // Ensure the cache object - if ( !this.cache[ unlock ] ) { - this.cache[ unlock ] = {}; - } - - return unlock; - }, - set: function( owner, data, value ) { - var prop, - // There may be an unlock assigned to this node, - // if there is no entry for this "owner", create one inline - // and set the unlock as though an owner entry had always existed - unlock = this.key( owner ), - cache = this.cache[ unlock ]; - - // Handle: [ owner, key, value ] args - if ( typeof data === "string" ) { - cache[ data ] = value; - - // Handle: [ owner, { properties } ] args - } else { - // Fresh assignments by object are shallow copied - if ( jQuery.isEmptyObject( cache ) ) { - jQuery.extend( this.cache[ unlock ], data ); - // Otherwise, copy the properties one-by-one to the cache object - } else { - for ( prop in data ) { - cache[ prop ] = data[ prop ]; - } - } - } - return cache; - }, - get: function( owner, key ) { - // Either a valid cache is found, or will be created. - // New caches will be created and the unlock returned, - // allowing direct access to the newly created - // empty data object. A valid owner object must be provided. - var cache = this.cache[ this.key( owner ) ]; - - return key === undefined ? - cache : cache[ key ]; - }, - access: function( owner, key, value ) { - var stored; - // In cases where either: - // - // 1. No key was specified - // 2. A string key was specified, but no value provided - // - // Take the "read" path and allow the get method to determine - // which value to return, respectively either: - // - // 1. The entire cache object - // 2. The data stored at the key - // - if ( key === undefined || - ((key && typeof key === "string") && value === undefined) ) { - - stored = this.get( owner, key ); - - return stored !== undefined ? - stored : this.get( owner, jQuery.camelCase(key) ); - } - - // [*]When the key is not a string, or both a key and value - // are specified, set or extend (existing objects) with either: - // - // 1. An object of properties - // 2. A key and value - // - this.set( owner, key, value ); - - // Since the "set" path can have two possible entry points - // return the expected data based on which path was taken[*] - return value !== undefined ? value : key; - }, - remove: function( owner, key ) { - var i, name, camel, - unlock = this.key( owner ), - cache = this.cache[ unlock ]; - - if ( key === undefined ) { - this.cache[ unlock ] = {}; - - } else { - // Support array or space separated string of keys - if ( jQuery.isArray( key ) ) { - // If "name" is an array of keys... - // When data is initially created, via ("key", "val") signature, - // keys will be converted to camelCase. - // Since there is no way to tell _how_ a key was added, remove - // both plain key and camelCase key. #12786 - // This will only penalize the array argument path. - name = key.concat( key.map( jQuery.camelCase ) ); - } else { - camel = jQuery.camelCase( key ); - // Try the string as a key before any manipulation - if ( key in cache ) { - name = [ key, camel ]; - } else { - // If a key with the spaces exists, use it. - // Otherwise, create an array by matching non-whitespace - name = camel; - name = name in cache ? - [ name ] : ( name.match( rnotwhite ) || [] ); - } - } - - i = name.length; - while ( i-- ) { - delete cache[ name[ i ] ]; - } - } - }, - hasData: function( owner ) { - return !jQuery.isEmptyObject( - this.cache[ owner[ this.expando ] ] || {} - ); - }, - discard: function( owner ) { - if ( owner[ this.expando ] ) { - delete this.cache[ owner[ this.expando ] ]; - } - } -}; -var data_priv = new Data(); - -var data_user = new Data(); - - - -/* - Implementation Summary - - 1. Enforce API surface and semantic compatibility with 1.9.x branch - 2. Improve the module's maintainability by reducing the storage - paths to a single mechanism. - 3. Use the same single mechanism to support "private" and "user" data. - 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) - 5. Avoid exposing implementation details on user objects (eg. expando properties) - 6. Provide a clear path for implementation upgrade to WeakMap in 2014 -*/ -var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - rmultiDash = /([A-Z])/g; - -function dataAttr( elem, key, data ) { - var name; - - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - // Only convert to a number if it doesn't change the string - +data + "" === data ? +data : - rbrace.test( data ) ? jQuery.parseJSON( data ) : - data; - } catch( e ) {} - - // Make sure we set the data so it isn't changed later - data_user.set( elem, key, data ); - } else { - data = undefined; - } - } - return data; -} - -jQuery.extend({ - hasData: function( elem ) { - return data_user.hasData( elem ) || data_priv.hasData( elem ); - }, - - data: function( elem, name, data ) { - return data_user.access( elem, name, data ); - }, - - removeData: function( elem, name ) { - data_user.remove( elem, name ); - }, - - // TODO: Now that all calls to _data and _removeData have been replaced - // with direct calls to data_priv methods, these can be deprecated. - _data: function( elem, name, data ) { - return data_priv.access( elem, name, data ); - }, - - _removeData: function( elem, name ) { - data_priv.remove( elem, name ); - } -}); - -jQuery.fn.extend({ - data: function( key, value ) { - var i, name, data, - elem = this[ 0 ], - attrs = elem && elem.attributes; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = data_user.get( elem ); - - if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { - i = attrs.length; - while ( i-- ) { - - // Support: IE11+ - // The attrs elements can be null (#14894) - if ( attrs[ i ] ) { - name = attrs[ i ].name; - if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.slice(5) ); - dataAttr( elem, name, data[ name ] ); - } - } - } - data_priv.set( elem, "hasDataAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each(function() { - data_user.set( this, key ); - }); - } - - return access( this, function( value ) { - var data, - camelKey = jQuery.camelCase( key ); - - // The calling jQuery object (element matches) is not empty - // (and therefore has an element appears at this[ 0 ]) and the - // `value` parameter was not undefined. An empty jQuery object - // will result in `undefined` for elem = this[ 0 ] which will - // throw an exception if an attempt to read a data cache is made. - if ( elem && value === undefined ) { - // Attempt to get data from the cache - // with the key as-is - data = data_user.get( elem, key ); - if ( data !== undefined ) { - return data; - } - - // Attempt to get data from the cache - // with the key camelized - data = data_user.get( elem, camelKey ); - if ( data !== undefined ) { - return data; - } - - // Attempt to "discover" the data in - // HTML5 custom data-* attrs - data = dataAttr( elem, camelKey, undefined ); - if ( data !== undefined ) { - return data; - } - - // We tried really hard, but the data doesn't exist. - return; - } - - // Set the data... - this.each(function() { - // First, attempt to store a copy or reference of any - // data that might've been store with a camelCased key. - var data = data_user.get( this, camelKey ); - - // For HTML5 data-* attribute interop, we have to - // store property names with dashes in a camelCase form. - // This might not apply to all properties...* - data_user.set( this, camelKey, value ); - - // *... In the case of properties that might _actually_ - // have dashes, we need to also store a copy of that - // unchanged property. - if ( key.indexOf("-") !== -1 && data !== undefined ) { - data_user.set( this, key, value ); - } - }); - }, null, value, arguments.length > 1, null, true ); - }, - - removeData: function( key ) { - return this.each(function() { - data_user.remove( this, key ); - }); - } -}); - - -jQuery.extend({ - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = data_priv.get( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || jQuery.isArray( data ) ) { - queue = data_priv.access( elem, type, jQuery.makeArray(data) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } - - if ( fn ) { - - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - // clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, - - // not intended for public consumption - generates a queueHooks object, or returns the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return data_priv.get( elem, key ) || data_priv.access( elem, key, { - empty: jQuery.Callbacks("once memory").add(function() { - data_priv.remove( elem, [ type + "queue", key ] ); - }) - }); - } -}); - -jQuery.fn.extend({ - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[0], type ); - } - - return data === undefined ? - this : - this.each(function() { - var queue = jQuery.queue( this, type, data ); - - // ensure a hooks for this queue - jQuery._queueHooks( this, type ); - - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - }); - }, - dequeue: function( type ) { - return this.each(function() { - jQuery.dequeue( this, type ); - }); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while ( i-- ) { - tmp = data_priv.get( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -}); -var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; - -var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; - -var isHidden = function( elem, el ) { - // isHidden might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); - }; - -var rcheckableType = (/^(?:checkbox|radio)$/i); - - - -(function() { - var fragment = document.createDocumentFragment(), - div = fragment.appendChild( document.createElement( "div" ) ), - input = document.createElement( "input" ); - - // #11217 - WebKit loses check when the name is after the checked attribute - // Support: Windows Web Apps (WWA) - // `name` and `type` need .setAttribute for WWA - input.setAttribute( "type", "radio" ); - input.setAttribute( "checked", "checked" ); - input.setAttribute( "name", "t" ); - - div.appendChild( input ); - - // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 - // old WebKit doesn't clone checked state correctly in fragments - support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Make sure textarea (and checkbox) defaultValue is properly cloned - // Support: IE9-IE11+ - div.innerHTML = ""; - support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; -})(); -var strundefined = typeof undefined; - - - -support.focusinBubbles = "onfocusin" in window; - - -var - rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; - -function returnTrue() { - return true; -} - -function returnFalse() { - return false; -} - -function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } -} - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - global: {}, - - add: function( elem, types, handler, data, selector ) { - - var handleObjIn, eventHandle, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = data_priv.get( elem ); - - // Don't attach events to noData or text/comment nodes (but allow plain objects) - if ( !elemData ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !(events = elemData.events) ) { - events = elemData.events = {}; - } - if ( !(eventHandle = elemData.handle) ) { - eventHandle = elemData.handle = function( e ) { - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ? - jQuery.event.dispatch.apply( elem, arguments ) : undefined; - }; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( rnotwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend({ - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join(".") - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !(handlers = events[ type ]) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var j, origCount, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = data_priv.hasData( elem ) && data_priv.get( elem ); - - if ( !elemData || !(events = elemData.events) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( rnotwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - delete elemData.handle; - data_priv.remove( elem, "events" ); - } - }, - - trigger: function( event, data, elem, onlyHandlers ) { - - var i, cur, tmp, bubbleType, ontype, handle, special, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; - - cur = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf(".") >= 0 ) { - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split("."); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf(":") < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join("."); - event.namespace_re = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === (elem.ownerDocument || document) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { - - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && jQuery.acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && - jQuery.acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name name as the event. - // Don't do default actions on window, that's where global variables be (#6170) - if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - elem[ type ](); - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - dispatch: function( event ) { - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( event ); - - var i, j, ret, matched, handleObj, - handlerQueue = [], - args = slice.call( arguments ), - handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[0] = event; - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { - - // Triggered event must either 1) have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). - if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) - .apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( (event.result = ret) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var i, matches, sel, handleObj, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - // Black-hole SVG instance trees (#13180) - // Avoid non-left-click bubbling in Firefox (#3861) - if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { - - for ( ; cur !== this; cur = cur.parentNode || this ) { - - // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.disabled !== true || event.type !== "click" ) { - matches = []; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (#13203) - sel = handleObj.selector + " "; - - if ( matches[ sel ] === undefined ) { - matches[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) >= 0 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matches[ sel ] ) { - matches.push( handleObj ); - } - } - if ( matches.length ) { - handlerQueue.push({ elem: cur, handlers: matches }); - } - } - } - } - - // Add the remaining (directly-bound) handlers - if ( delegateCount < handlers.length ) { - handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); - } - - return handlerQueue; - }, - - // Includes some event props shared by KeyEvent and MouseEvent - props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), - - fixHooks: {}, - - keyHooks: { - props: "char charCode key keyCode".split(" "), - filter: function( event, original ) { - - // Add which for key events - if ( event.which == null ) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } - - return event; - } - }, - - mouseHooks: { - props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "), - filter: function( event, original ) { - var eventDoc, doc, body, - button = original.button; - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && original.clientX != null ) { - eventDoc = event.target.ownerDocument || document; - doc = eventDoc.documentElement; - body = eventDoc.body; - - event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && button !== undefined ) { - event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); - } - - return event; - } - }, - - fix: function( event ) { - if ( event[ jQuery.expando ] ) { - return event; - } - - // Create a writable copy of the event object and normalize some properties - var i, prop, copy, - type = event.type, - originalEvent = event, - fixHook = this.fixHooks[ type ]; - - if ( !fixHook ) { - this.fixHooks[ type ] = fixHook = - rmouseEvent.test( type ) ? this.mouseHooks : - rkeyEvent.test( type ) ? this.keyHooks : - {}; - } - copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; - - event = new jQuery.Event( originalEvent ); - - i = copy.length; - while ( i-- ) { - prop = copy[ i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Support: Cordova 2.5 (WebKit) (#13255) - // All events should have a target; Cordova deviceready doesn't - if ( !event.target ) { - event.target = document; - } - - // Support: Safari 6.0+, Chrome < 28 - // Target should not be a text node (#504, #13143) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; - }, - - special: { - load: { - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - focus: { - // Fire native event if possible so blur/focus sequence is correct - trigger: function() { - if ( this !== safeActiveElement() && this.focus ) { - this.focus(); - return false; - } - }, - delegateType: "focusin" - }, - blur: { - trigger: function() { - if ( this === safeActiveElement() && this.blur ) { - this.blur(); - return false; - } - }, - delegateType: "focusout" - }, - click: { - // For checkbox, fire native event so checked state will be right - trigger: function() { - if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { - this.click(); - return false; - } - }, - - // For cross-browser consistency, don't fire native .click() on links - _default: function( event ) { - return jQuery.nodeName( event.target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined && event.originalEvent ) { - event.originalEvent.returnValue = event.result; - } - } - } - }, - - simulate: function( type, elem, event, bubble ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true, - originalEvent: {} - } - ); - if ( bubble ) { - jQuery.event.trigger( e, null, elem ); - } else { - jQuery.event.dispatch.call( elem, e ); - } - if ( e.isDefaultPrevented() ) { - event.preventDefault(); - } - } -}; - -jQuery.removeEvent = function( elem, type, handle ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); - } -}; - -jQuery.Event = function( src, props ) { - // Allow instantiation without the 'new' keyword - if ( !(this instanceof jQuery.Event) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented || - src.defaultPrevented === undefined && - // Support: Android < 4.0 - src.returnValue === false ? - returnTrue : - returnFalse; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || jQuery.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - - preventDefault: function() { - var e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - - if ( e && e.preventDefault ) { - e.preventDefault(); - } - }, - stopPropagation: function() { - var e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - - if ( e && e.stopPropagation ) { - e.stopPropagation(); - } - }, - stopImmediatePropagation: function() { - var e = this.originalEvent; - - this.isImmediatePropagationStopped = returnTrue; - - if ( e && e.stopImmediatePropagation ) { - e.stopImmediatePropagation(); - } - - this.stopPropagation(); - } -}; - -// Create mouseenter/leave events using mouseover/out and event-time checks -// Support: Chrome 15+ -jQuery.each({ - mouseenter: "mouseover", - mouseleave: "mouseout", - pointerenter: "pointerover", - pointerleave: "pointerout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; - - // For mousenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || (related !== target && !jQuery.contains( target, related )) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -}); - -// Create "bubbling" focus and blur events -// Support: Firefox, Chrome, Safari -if ( !support.focusinBubbles ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler on the document while someone wants focusin/focusout - var handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - var doc = this.ownerDocument || this, - attaches = data_priv.access( doc, fix ); - - if ( !attaches ) { - doc.addEventListener( orig, handler, true ); - } - data_priv.access( doc, fix, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this, - attaches = data_priv.access( doc, fix ) - 1; - - if ( !attaches ) { - doc.removeEventListener( orig, handler, true ); - data_priv.remove( doc, fix ); - - } else { - data_priv.access( doc, fix, attaches ); - } - } - }; - }); -} - -jQuery.fn.extend({ - - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - this.on( type, selector, data, types[ type ], one ); - } - return this; - } - - if ( data == null && fn == null ) { - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return this; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return this.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - }); - }, - one: function( types, selector, data, fn ) { - return this.on( types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each(function() { - jQuery.event.remove( this, types, fn, selector ); - }); - }, - - trigger: function( type, data ) { - return this.each(function() { - jQuery.event.trigger( type, data, this ); - }); - }, - triggerHandler: function( type, data ) { - var elem = this[0]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } -}); - - -var - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, - rtagName = /<([\w:]+)/, - rhtml = /<|&#?\w+;/, - rnoInnerhtml = /<(?:script|style|link)/i, - // checked="checked" or checked - rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, - rscriptType = /^$|\/(?:java|ecma)script/i, - rscriptTypeMasked = /^true\/(.*)/, - rcleanScript = /^\s*\s*$/g, - - // We have to close these tags to support XHTML (#13200) - wrapMap = { - - // Support: IE 9 - option: [ 1, "" ], - - thead: [ 1, "", "
    " ], - col: [ 2, "", "
    " ], - tr: [ 2, "", "
    " ], - td: [ 3, "", "
    " ], - - _default: [ 0, "", "" ] - }; - -// Support: IE 9 -wrapMap.optgroup = wrapMap.option; - -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// Support: 1.x compatibility -// Manipulating tables requires a tbody -function manipulationTarget( elem, content ) { - return jQuery.nodeName( elem, "table" ) && - jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? - - elem.getElementsByTagName("tbody")[0] || - elem.appendChild( elem.ownerDocument.createElement("tbody") ) : - elem; -} - -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type; - return elem; -} -function restoreScript( elem ) { - var match = rscriptTypeMasked.exec( elem.type ); - - if ( match ) { - elem.type = match[ 1 ]; - } else { - elem.removeAttribute("type"); - } - - return elem; -} - -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - data_priv.set( - elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) - ); - } -} - -function cloneCopyEvent( src, dest ) { - var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; - - if ( dest.nodeType !== 1 ) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if ( data_priv.hasData( src ) ) { - pdataOld = data_priv.access( src ); - pdataCur = data_priv.set( dest, pdataOld ); - events = pdataOld.events; - - if ( events ) { - delete pdataCur.handle; - pdataCur.events = {}; - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - } - - // 2. Copy user data - if ( data_user.hasData( src ) ) { - udataOld = data_user.access( src ); - udataCur = jQuery.extend( {}, udataOld ); - - data_user.set( dest, udataCur ); - } -} - -function getAll( context, tag ) { - var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : - context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : - []; - - return tag === undefined || tag && jQuery.nodeName( context, tag ) ? - jQuery.merge( [ context ], ret ) : - ret; -} - -// Support: IE >= 9 -function fixInput( src, dest ) { - var nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} - -jQuery.extend({ - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var i, l, srcElements, destElements, - clone = elem.cloneNode( true ), - inPage = jQuery.contains( elem.ownerDocument, elem ); - - // Support: IE >= 9 - // Fix Cloning issues - if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && - !jQuery.isXMLDoc( elem ) ) { - - // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - fixInput( srcElements[ i ], destElements[ i ] ); - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - cloneCopyEvent( srcElements[ i ], destElements[ i ] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - // Return the cloned set - return clone; - }, - - buildFragment: function( elems, context, scripts, selection ) { - var elem, tmp, tag, wrap, contains, j, - fragment = context.createDocumentFragment(), - nodes = [], - i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( jQuery.type( elem ) === "object" ) { - // Support: QtWebKit - // jQuery.merge because push.apply(_, arraylike) throws - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || fragment.appendChild( context.createElement("div") ); - - // Deserialize a standard representation - tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[ 2 ]; - - // Descend through wrappers to the right content - j = wrap[ 0 ]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Support: QtWebKit - // jQuery.merge because push.apply(_, arraylike) throws - jQuery.merge( nodes, tmp.childNodes ); - - // Remember the top-level container - tmp = fragment.firstChild; - - // Fixes #12346 - // Support: Webkit, IE - tmp.textContent = ""; - } - } - } - - // Remove wrapper from fragment - fragment.textContent = ""; - - i = 0; - while ( (elem = nodes[ i++ ]) ) { - - // #4087 - If origin and destination elements are the same, and this is - // that element, do not do anything - if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { - continue; - } - - contains = jQuery.contains( elem.ownerDocument, elem ); - - // Append to fragment - tmp = getAll( fragment.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( contains ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( (elem = tmp[ j++ ]) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - return fragment; - }, - - cleanData: function( elems ) { - var data, elem, type, key, - special = jQuery.event.special, - i = 0; - - for ( ; (elem = elems[ i ]) !== undefined; i++ ) { - if ( jQuery.acceptData( elem ) ) { - key = elem[ data_priv.expando ]; - - if ( key && (data = data_priv.cache[ key ]) ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - if ( data_priv.cache[ key ] ) { - // Discard any remaining `private` data - delete data_priv.cache[ key ]; - } - } - } - // Discard any remaining `user` data - delete data_user.cache[ elem[ data_user.expando ] ]; - } - } -}); - -jQuery.fn.extend({ - text: function( value ) { - return access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().each(function() { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - this.textContent = value; - } - }); - }, null, value, arguments.length ); - }, - - append: function() { - return this.domManip( arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - }); - }, - - prepend: function() { - return this.domManip( arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - }); - }, - - before: function() { - return this.domManip( arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - }); - }, - - after: function() { - return this.domManip( arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - }); - }, - - remove: function( selector, keepData /* Internal Use Only */ ) { - var elem, - elems = selector ? jQuery.filter( selector, this ) : this, - i = 0; - - for ( ; (elem = elems[i]) != null; i++ ) { - if ( !keepData && elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem ) ); - } - - if ( elem.parentNode ) { - if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { - setGlobalEval( getAll( elem, "script" ) ); - } - elem.parentNode.removeChild( elem ); - } - } - - return this; - }, - - empty: function() { - var elem, - i = 0; - - for ( ; (elem = this[i]) != null; i++ ) { - if ( elem.nodeType === 1 ) { - - // Prevent memory leaks - jQuery.cleanData( getAll( elem, false ) ); - - // Remove any remaining nodes - elem.textContent = ""; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map(function() { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - }); - }, - - html: function( value ) { - return access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined && elem.nodeType === 1 ) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - - value = value.replace( rxhtmlTag, "<$1>" ); - - try { - for ( ; i < l; i++ ) { - elem = this[ i ] || {}; - - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch( e ) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var arg = arguments[ 0 ]; - - // Make the changes, replacing each context element with the new content - this.domManip( arguments, function( elem ) { - arg = this.parentNode; - - jQuery.cleanData( getAll( this ) ); - - if ( arg ) { - arg.replaceChild( elem, this ); - } - }); - - // Force removal if there was no new content (e.g., from empty arguments) - return arg && (arg.length || arg.nodeType) ? this : this.remove(); - }, - - detach: function( selector ) { - return this.remove( selector, true ); - }, - - domManip: function( args, callback ) { - - // Flatten any nested arrays - args = concat.apply( [], args ); - - var fragment, first, scripts, hasScripts, node, doc, - i = 0, - l = this.length, - set = this, - iNoClone = l - 1, - value = args[ 0 ], - isFunction = jQuery.isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( isFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return this.each(function( index ) { - var self = set.eq( index ); - if ( isFunction ) { - args[ 0 ] = value.call( this, index, self.html() ); - } - self.domManip( args, callback ); - }); - } - - if ( l ) { - fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - if ( first ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - // Support: QtWebKit - // jQuery.merge because push.apply(_, arraylike) throws - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( this[ i ], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { - - if ( node.src ) { - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl ) { - jQuery._evalUrl( node.src ); - } - } else { - jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); - } - } - } - } - } - } - - return this; - } -}); - -jQuery.each({ - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1, - i = 0; - - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone( true ); - jQuery( insert[ i ] )[ original ]( elems ); - - // Support: QtWebKit - // .get() because push.apply(_, arraylike) throws - push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; -}); - - -var iframe, - elemdisplay = {}; - -/** - * Retrieve the actual display of a element - * @param {String} name nodeName of the element - * @param {Object} doc Document object - */ -// Called only from within defaultDisplay -function actualDisplay( name, doc ) { - var style, - elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), - - // getDefaultComputedStyle might be reliably used only on attached element - display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? - - // Use of this method is a temporary fix (more like optmization) until something better comes along, - // since it was removed from specification and supported only in FF - style.display : jQuery.css( elem[ 0 ], "display" ); - - // We don't have any data stored on the element, - // so use "detach" method as fast way to get rid of the element - elem.detach(); - - return display; -} - -/** - * Try to determine the default display value of an element - * @param {String} nodeName - */ -function defaultDisplay( nodeName ) { - var doc = document, - display = elemdisplay[ nodeName ]; - - if ( !display ) { - display = actualDisplay( nodeName, doc ); - - // If the simple way fails, read from inside an iframe - if ( display === "none" || !display ) { - - // Use the already-created iframe if possible - iframe = (iframe || jQuery( "