From 29c9ee2c467d9c2654f37c802ef413c6da26ed65 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 6 Apr 2016 16:33:42 -0400 Subject: [PATCH 001/269] [maven-release-plugin] prepare release mitreid-connect-1.2.6 --- openid-connect-client/pom.xml | 2 +- openid-connect-common/pom.xml | 2 +- openid-connect-server-webapp/pom.xml | 2 +- openid-connect-server/pom.xml | 2 +- pom.xml | 2 +- uma-server-webapp/pom.xml | 2 +- uma-server/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml index 42b7ea8cd3..e7270e4a24 100644 --- a/openid-connect-client/pom.xml +++ b/openid-connect-client/pom.xml @@ -21,7 +21,7 @@ openid-connect-parent org.mitre - 1.2.6-SNAPSHOT + 1.2.6 .. openid-connect-client diff --git a/openid-connect-common/pom.xml b/openid-connect-common/pom.xml index 093be49a2e..24539a72ad 100644 --- a/openid-connect-common/pom.xml +++ b/openid-connect-common/pom.xml @@ -21,7 +21,7 @@ openid-connect-parent org.mitre - 1.2.6-SNAPSHOT + 1.2.6 .. openid-connect-common diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index 79ddae9070..b40694a1bc 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.2.6-SNAPSHOT + 1.2.6 openid-connect-server-webapp war diff --git a/openid-connect-server/pom.xml b/openid-connect-server/pom.xml index b50b1a089f..8ce1645919 100644 --- a/openid-connect-server/pom.xml +++ b/openid-connect-server/pom.xml @@ -22,7 +22,7 @@ org.mitre openid-connect-parent - 1.2.6-SNAPSHOT + 1.2.6 .. diff --git a/pom.xml b/pom.xml index 0f00ea1175..f4154995dd 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 4.0.0 org.mitre openid-connect-parent - 1.2.6-SNAPSHOT + 1.2.6 MITREid Connect pom diff --git a/uma-server-webapp/pom.xml b/uma-server-webapp/pom.xml index 39db3d67d0..7480e016c7 100644 --- a/uma-server-webapp/pom.xml +++ b/uma-server-webapp/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.2.6-SNAPSHOT + 1.2.6 .. uma-server-webapp diff --git a/uma-server/pom.xml b/uma-server/pom.xml index 81b6dcb8b6..ca3ddb16d4 100644 --- a/uma-server/pom.xml +++ b/uma-server/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.2.6-SNAPSHOT + 1.2.6 .. uma-server From 58724aa6dc5abc08d6b216470c92f76e34092f49 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 6 Apr 2016 16:33:45 -0400 Subject: [PATCH 002/269] [maven-release-plugin] prepare for next development iteration --- openid-connect-client/pom.xml | 2 +- openid-connect-common/pom.xml | 2 +- openid-connect-server-webapp/pom.xml | 2 +- openid-connect-server/pom.xml | 2 +- pom.xml | 2 +- uma-server-webapp/pom.xml | 2 +- uma-server/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml index e7270e4a24..d3ae927710 100644 --- a/openid-connect-client/pom.xml +++ b/openid-connect-client/pom.xml @@ -21,7 +21,7 @@ openid-connect-parent org.mitre - 1.2.6 + 1.2.7-SNAPSHOT .. openid-connect-client diff --git a/openid-connect-common/pom.xml b/openid-connect-common/pom.xml index 24539a72ad..a00c5a530a 100644 --- a/openid-connect-common/pom.xml +++ b/openid-connect-common/pom.xml @@ -21,7 +21,7 @@ openid-connect-parent org.mitre - 1.2.6 + 1.2.7-SNAPSHOT .. openid-connect-common diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index b40694a1bc..3053bf020e 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.2.6 + 1.2.7-SNAPSHOT openid-connect-server-webapp war diff --git a/openid-connect-server/pom.xml b/openid-connect-server/pom.xml index 8ce1645919..7169c5ae85 100644 --- a/openid-connect-server/pom.xml +++ b/openid-connect-server/pom.xml @@ -22,7 +22,7 @@ org.mitre openid-connect-parent - 1.2.6 + 1.2.7-SNAPSHOT .. diff --git a/pom.xml b/pom.xml index f4154995dd..e66245dd46 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 4.0.0 org.mitre openid-connect-parent - 1.2.6 + 1.2.7-SNAPSHOT MITREid Connect pom diff --git a/uma-server-webapp/pom.xml b/uma-server-webapp/pom.xml index 7480e016c7..436fafedd3 100644 --- a/uma-server-webapp/pom.xml +++ b/uma-server-webapp/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.2.6 + 1.2.7-SNAPSHOT .. uma-server-webapp diff --git a/uma-server/pom.xml b/uma-server/pom.xml index ca3ddb16d4..f7d805b060 100644 --- a/uma-server/pom.xml +++ b/uma-server/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.2.6 + 1.2.7-SNAPSHOT .. uma-server From 326ce4cb6fac92988eba8e8de85b737c8f2b57e1 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 19 May 2016 15:10:18 -0500 Subject: [PATCH 003/269] trying out codecov.io for code coverage tests --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 3be2ae5cdf..25cbfdba47 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,3 +4,7 @@ jdk: - oraclejdk7 - openjdk7 sudo: false + +after_success: + - bash <(curl -s https://codecov.io/bash) + From c31f42c3f389f239803c041c64732ea61fa26e68 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 5 Jul 2016 14:39:22 -0400 Subject: [PATCH 004/269] updated versions to 1.3 --- openid-connect-client/pom.xml | 2 +- openid-connect-common/pom.xml | 2 +- openid-connect-server-webapp/pom.xml | 3 ++- openid-connect-server/pom.xml | 2 +- pom.xml | 2 +- uma-server-webapp/pom.xml | 2 +- uma-server/pom.xml | 2 +- 7 files changed, 8 insertions(+), 7 deletions(-) diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml index d3ae927710..4c82709ee6 100644 --- a/openid-connect-client/pom.xml +++ b/openid-connect-client/pom.xml @@ -21,7 +21,7 @@ openid-connect-parent org.mitre - 1.2.7-SNAPSHOT + 1.3.0-SNAPSHOT .. openid-connect-client diff --git a/openid-connect-common/pom.xml b/openid-connect-common/pom.xml index a00c5a530a..9e55013e6c 100644 --- a/openid-connect-common/pom.xml +++ b/openid-connect-common/pom.xml @@ -21,7 +21,7 @@ openid-connect-parent org.mitre - 1.2.7-SNAPSHOT + 1.3.0-SNAPSHOT .. openid-connect-common diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index 3053bf020e..4f35ad1c57 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.2.7-SNAPSHOT + 1.3.0-SNAPSHOT openid-connect-server-webapp war @@ -134,4 +134,5 @@ HikariCP + Deployable package of the OpenID Connect server diff --git a/openid-connect-server/pom.xml b/openid-connect-server/pom.xml index 7169c5ae85..c80649c340 100644 --- a/openid-connect-server/pom.xml +++ b/openid-connect-server/pom.xml @@ -22,7 +22,7 @@ org.mitre openid-connect-parent - 1.2.7-SNAPSHOT + 1.3.0-SNAPSHOT .. diff --git a/pom.xml b/pom.xml index e66245dd46..696cf6baf9 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 4.0.0 org.mitre openid-connect-parent - 1.2.7-SNAPSHOT + 1.3.0-SNAPSHOT MITREid Connect pom diff --git a/uma-server-webapp/pom.xml b/uma-server-webapp/pom.xml index 436fafedd3..9a82d26121 100644 --- a/uma-server-webapp/pom.xml +++ b/uma-server-webapp/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.2.7-SNAPSHOT + 1.3.0-SNAPSHOT .. uma-server-webapp diff --git a/uma-server/pom.xml b/uma-server/pom.xml index f7d805b060..8ea638908b 100644 --- a/uma-server/pom.xml +++ b/uma-server/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.2.7-SNAPSHOT + 1.3.0-SNAPSHOT .. uma-server From 2d495bd0bdbe4a84156fe624f5e94ee7476f6a78 Mon Sep 17 00:00:00 2001 From: Mark Janssen Date: Sat, 23 May 2015 20:44:46 +0200 Subject: [PATCH 005/269] Java 8 language level --- .travis.yml | 3 --- pom.xml | 4 +++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 25cbfdba47..da6fe0877a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,7 @@ language: java jdk: - oraclejdk8 - - oraclejdk7 - - openjdk7 sudo: false after_success: - bash <(curl -s https://codecov.io/bash) - diff --git a/pom.xml b/pom.xml index 696cf6baf9..828af528af 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,9 @@ - 1.7 + 1.8 + 4.1.1.RELEASE + 3.2.5.RELEASE 1.7.12 A reference implementation of OpenID Connect (http://openid.net/connect/), OAuth 2.0, and UMA built on top of Java, Spring, and Spring Security. The project contains a fully functioning server, client, and utility library. From 832f5c0199a61faf09f06ff925135d38162fa2c2 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 5 Jul 2016 15:01:15 -0400 Subject: [PATCH 006/269] removed redundant profile for javadoc lint --- pom.xml | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/pom.xml b/pom.xml index 828af528af..49a73c6748 100644 --- a/pom.xml +++ b/pom.xml @@ -144,6 +144,7 @@ MITREid Connect v. ${project.version} MITREid Connect v. ${project.version} ${basedir}/src/main/javadoc/overview.html + -Xdoclint:none @@ -529,27 +530,4 @@ - - - - - 1.8 - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9 - - -Xdoclint:none - - - - - - - - From f21c374e8a9b0cec7622ed0631e5bb28d872372a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= Date: Mon, 21 Mar 2016 18:23:36 +0100 Subject: [PATCH 007/269] Add Editorconfig for minimal code style support --- .editorconfig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..a9ae158608 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# 4 space - Tab indentation +[*.{java,xml,js,html}] +indent_style = tab +indent_size = 4 From e08b7f89fcdf42f55f0ef6e403a148828ed5ba0f Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 7 Jul 2016 16:30:15 -0400 Subject: [PATCH 008/269] fixed bouncy castle dependency range, closes #1084 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 49a73c6748..fb15f0844f 100644 --- a/pom.xml +++ b/pom.xml @@ -483,7 +483,7 @@ org.bouncycastle bcprov-jdk15on - [1.52,] + [1.52,) From fa4722cdd8a2d2d3ac770174e43f795b4be443fd Mon Sep 17 00:00:00 2001 From: jimmytheneutrino Date: Tue, 5 Jul 2016 03:00:31 +0300 Subject: [PATCH 009/269] shortTopbarTitle --- .../connect/config/ConfigurationPropertiesBean.java | 13 +++++++++++++ .../config/ConfigurationPropertiesBeanTest.java | 9 +++++++++ .../src/main/webapp/WEB-INF/tags/topbar.tag | 8 +++++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ConfigurationPropertiesBean.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ConfigurationPropertiesBean.java index 3af68e3a94..1aa8588b37 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ConfigurationPropertiesBean.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ConfigurationPropertiesBean.java @@ -49,6 +49,8 @@ public class ConfigurationPropertiesBean { private String issuer; private String topbarTitle; + + private String shortTopbarTitle; private String logoImageUrl; @@ -118,6 +120,17 @@ public String getTopbarTitle() { public void setTopbarTitle(String topbarTitle) { this.topbarTitle = topbarTitle; } + + /** + * @return If shortTopbarTitle is undefined, returns topbarTitle. + */ + public String getShortTopbarTitle() { + return shortTopbarTitle == null ? topbarTitle : shortTopbarTitle; + } + + public void setShortTopbarTitle(String shortTopbarTitle) { + this.shortTopbarTitle = shortTopbarTitle; + } /** * @return the logoImageUrl diff --git a/openid-connect-common/src/test/java/org/mitre/openid/connect/config/ConfigurationPropertiesBeanTest.java b/openid-connect-common/src/test/java/org/mitre/openid/connect/config/ConfigurationPropertiesBeanTest.java index 7391d23d27..a06915e1ae 100644 --- a/openid-connect-common/src/test/java/org/mitre/openid/connect/config/ConfigurationPropertiesBeanTest.java +++ b/openid-connect-common/src/test/java/org/mitre/openid/connect/config/ConfigurationPropertiesBeanTest.java @@ -134,5 +134,14 @@ public void testCheckForHttpsIssuerHttpsTrueFlag() { } } + + @Test + public void testShortTopbarTitle() { + ConfigurationPropertiesBean bean = new ConfigurationPropertiesBean(); + bean.setTopbarTitle("LONG"); + assertEquals("LONG", bean.getShortTopbarTitle()); + bean.setShortTopbarTitle("SHORT"); + assertEquals("SHORT", bean.getShortTopbarTitle()); + } } diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/topbar.tag b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/topbar.tag index 295d7d4bb2..9a2b0f7827 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/topbar.tag +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/topbar.tag @@ -34,7 +34,13 @@ - ${config.topbarTitle} + + + + ${config.shortTopbarTitle} + ${config.topbarTitle} + +
From 83d7627ed021940fe2cee565cfe43857f5f2f606 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sun, 24 Jul 2016 16:49:46 -0400 Subject: [PATCH 055/269] serialize phone_number and phone_number_verified, closes #1030 --- .../java/org/mitre/openid/connect/model/DefaultUserInfo.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java index 8e9900cae3..1bc96e95a0 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java @@ -497,6 +497,9 @@ public static UserInfo fromJson(JsonObject obj) { ui.setEmail(nullSafeGetString(obj, "email")); ui.setEmailVerified(obj.has("email_verified") && obj.get("email_verified").isJsonPrimitive() ? obj.get("email_verified").getAsBoolean() : null); + ui.setPhoneNumber(nullSafeGetString(obj, "phone_number")); + ui.setPhoneNumberVerified(obj.has("phone_number_verified") && obj.get("phone_number_verified").isJsonPrimitive() ? obj.get("phone_number_verified").getAsBoolean() : null); + if (obj.has("address") && obj.get("address").isJsonObject()) { JsonObject addr = obj.get("address").getAsJsonObject(); ui.setAddress(new DefaultAddress()); From 2cc90ba5f2a989a9672d9b87916d14d62611d221 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sun, 24 Jul 2016 17:06:29 -0400 Subject: [PATCH 056/269] created PKCE algorithm class --- .../oauth2/model/ClientDetailsEntity.java | 20 ++++++- .../org/mitre/oauth2/model/PKCEAlgorithm.java | 53 +++++++++++++++++++ .../mitre/oauth2/model/RegisteredClient.java | 16 ++++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 openid-connect-common/src/main/java/org/mitre/oauth2/model/PKCEAlgorithm.java diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java index 8de9606ce1..6d94e6d1d8 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java @@ -56,6 +56,7 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.provider.ClientDetails; +import com.nimbusds.jose.Algorithm; import com.nimbusds.jose.EncryptionMethod; import com.nimbusds.jose.JWEAlgorithm; import com.nimbusds.jose.JWSAlgorithm; @@ -149,6 +150,9 @@ public class ClientDetailsEntity implements ClientDetails { /** Software statement **/ private JWT softwareStatement; + + /** PKCE **/ + private PKCEAlgorithm codeChallengeMethod; public enum AuthMethod { SECRET_POST("client_secret_post"), @@ -231,7 +235,7 @@ public static SubjectType getByValue(String value) { return lookup.get(value); } } - + /** * Create a blank ClientDetailsEntity */ @@ -1010,4 +1014,18 @@ public void setSoftwareStatement(JWT softwareStatement) { this.softwareStatement = softwareStatement; } + /** + * @return the codeChallengeMethod + */ + public PKCEAlgorithm getCodeChallengeMethod() { + return codeChallengeMethod; + } + + /** + * @param codeChallengeMethod the codeChallengeMethod to set + */ + public void setCodeChallengeMethod(PKCEAlgorithm codeChallengeMethod) { + this.codeChallengeMethod = codeChallengeMethod; + } + } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/PKCEAlgorithm.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/PKCEAlgorithm.java new file mode 100644 index 0000000000..7a507df361 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/PKCEAlgorithm.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright 2016 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.oauth2.model; + +import com.nimbusds.jose.Algorithm; +import com.nimbusds.jose.Requirement; + +/** + * @author jricher + * + */ +public final class PKCEAlgorithm extends Algorithm { + + public static final PKCEAlgorithm plain = new PKCEAlgorithm("plain", Requirement.REQUIRED); + + public static final PKCEAlgorithm S256 = new PKCEAlgorithm("S256", Requirement.OPTIONAL); + + public PKCEAlgorithm(String name, Requirement req) { + super(name, req); + } + + public PKCEAlgorithm(String name) { + super(name, null); + } + + public static PKCEAlgorithm parse(final String s) { + if (s.equals(plain.getName())) { + return plain; + } else if (s.equals(S256.getName())) { + return S256; + } else { + return new PKCEAlgorithm(s); + } + } + + + +} diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java index dda6d4a1b8..e90f1d9284 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java @@ -815,6 +815,22 @@ public JWT getSoftwareStatement() { public void setSoftwareStatement(JWT softwareStatement) { client.setSoftwareStatement(softwareStatement); } + + /** + * @return + * @see org.mitre.oauth2.model.ClientDetailsEntity#getCodeChallengeMethod() + */ + public PKCEAlgorithm getCodeChallengeMethod() { + return client.getCodeChallengeMethod(); + } + + /** + * @param codeChallengeMethod + * @see org.mitre.oauth2.model.ClientDetailsEntity#setCodeChallengeMethod(org.mitre.oauth2.model.PKCEAlgorithm) + */ + public void setCodeChallengeMethod(PKCEAlgorithm codeChallengeMethod) { + client.setCodeChallengeMethod(codeChallengeMethod); + } /** * @return the src From 5dcda2812e8557b231104dbdfef2b9ee74d5f93e Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sun, 24 Jul 2016 17:45:04 -0400 Subject: [PATCH 057/269] added code challenge method to client model (properly this time) --- .../oauth2/model/ClientDetailsEntity.java | 4 ++ .../convert/PKCEAlgorithmStringConverter.java | 53 +++++++++++++++++++ .../db/tables/hsql_database_tables.sql | 2 + 3 files changed, 59 insertions(+) create mode 100644 openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/PKCEAlgorithmStringConverter.java diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java index 6d94e6d1d8..ed54cbc508 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java @@ -52,6 +52,7 @@ import org.mitre.oauth2.model.convert.JWKSetStringConverter; import org.mitre.oauth2.model.convert.JWSAlgorithmStringConverter; import org.mitre.oauth2.model.convert.JWTStringConverter; +import org.mitre.oauth2.model.convert.PKCEAlgorithmStringConverter; import org.mitre.oauth2.model.convert.SimpleGrantedAuthorityStringConverter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.provider.ClientDetails; @@ -1017,6 +1018,9 @@ public void setSoftwareStatement(JWT softwareStatement) { /** * @return the codeChallengeMethod */ + @Basic + @Column(name = "code_challenge_method") + @Convert(converter = PKCEAlgorithmStringConverter.class) public PKCEAlgorithm getCodeChallengeMethod() { return codeChallengeMethod; } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/PKCEAlgorithmStringConverter.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/PKCEAlgorithmStringConverter.java new file mode 100644 index 0000000000..51eee1c0f2 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/PKCEAlgorithmStringConverter.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright 2016 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.oauth2.model.convert; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +import org.mitre.oauth2.model.PKCEAlgorithm; + +/** + * @author jricher + * + */ +@Converter +public class PKCEAlgorithmStringConverter implements AttributeConverter { + + @Override + public String convertToDatabaseColumn(PKCEAlgorithm attribute) { + if (attribute != null) { + return attribute.getName(); + } else { + return null; + } + } + + /* (non-Javadoc) + * @see javax.persistence.AttributeConverter#convertToEntityAttribute(java.lang.Object) + */ + @Override + public PKCEAlgorithm convertToEntityAttribute(String dbData) { + if (dbData != null) { + return PKCEAlgorithm.parse(dbData); + } else { + return null; + } + } + +} diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/hsql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/tables/hsql_database_tables.sql index e81927dffd..382a1934c3 100644 --- a/openid-connect-server-webapp/src/main/resources/db/tables/hsql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/tables/hsql_database_tables.sql @@ -171,6 +171,8 @@ CREATE TABLE IF NOT EXISTS client_details ( software_statement VARCHAR(4096), + code_challenge_method VARCHAR(256), + UNIQUE (client_id) ); From ac0cafe7b3fc05189dfab28cd5bf879bc930affc Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sun, 24 Jul 2016 17:45:43 -0400 Subject: [PATCH 058/269] parse and process PKCE requests --- .../DefaultOAuth2ProviderTokenService.java | 48 +++++++++++++++++-- .../request/ConnectOAuth2RequestFactory.java | 13 ++++- .../request/ConnectRequestParameters.java | 7 ++- 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java index 113bf3c370..ee66603f79 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java @@ -19,6 +19,13 @@ */ package org.mitre.oauth2.service.impl; +import static org.mitre.openid.connect.request.ConnectRequestParameters.CODE_CHALLENGE; +import static org.mitre.openid.connect.request.ConnectRequestParameters.CODE_CHALLENGE_METHOD; +import static org.mitre.openid.connect.request.ConnectRequestParameters.CODE_VERIFIER; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Collection; import java.util.Date; import java.util.HashSet; @@ -30,6 +37,7 @@ import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.oauth2.model.OAuth2RefreshTokenEntity; +import org.mitre.oauth2.model.PKCEAlgorithm; import org.mitre.oauth2.model.SystemScope; import org.mitre.oauth2.repository.AuthenticationHolderRepository; import org.mitre.oauth2.repository.OAuth2TokenRepository; @@ -44,9 +52,9 @@ import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.common.exceptions.InvalidClientException; +import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; -import org.springframework.security.oauth2.provider.ClientAlreadyExistsException; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.TokenRequest; @@ -54,6 +62,7 @@ import org.springframework.stereotype.Service; import com.google.common.collect.Sets; +import com.nimbusds.jose.util.Base64URL; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.PlainJWT; @@ -169,14 +178,43 @@ private OAuth2RefreshTokenEntity clearExpiredRefreshToken(OAuth2RefreshTokenEnti public OAuth2AccessTokenEntity createAccessToken(OAuth2Authentication authentication) throws AuthenticationException, InvalidClientException { if (authentication != null && authentication.getOAuth2Request() != null) { // look up our client - OAuth2Request clientAuth = authentication.getOAuth2Request(); + OAuth2Request request = authentication.getOAuth2Request(); - ClientDetailsEntity client = clientDetailsService.loadClientByClientId(clientAuth.getClientId()); + ClientDetailsEntity client = clientDetailsService.loadClientByClientId(request.getClientId()); if (client == null) { - throw new InvalidClientException("Client not found: " + clientAuth.getClientId()); + throw new InvalidClientException("Client not found: " + request.getClientId()); + } + + + // handle the PKCE code challenge if present + if (request.getExtensions().containsKey(CODE_CHALLENGE)) { + String challenge = (String) request.getExtensions().get(CODE_CHALLENGE); + PKCEAlgorithm alg = PKCEAlgorithm.parse((String) request.getExtensions().get(CODE_CHALLENGE_METHOD)); + + String verifier = request.getRequestParameters().get(CODE_VERIFIER); + + if (alg.equals(PKCEAlgorithm.plain)) { + // do a direct string comparison + if (!challenge.equals(verifier)) { + throw new InvalidRequestException("Code challenge and verifier do not match"); + } + } else if (alg.equals(PKCEAlgorithm.S256)) { + // hash the verifier + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + String hash = Base64URL.encode(digest.digest(verifier.getBytes(StandardCharsets.US_ASCII))).toString(); + if (!challenge.equals(hash)) { + throw new InvalidRequestException("Code challenge and verifier do not match"); + } + } catch (NoSuchAlgorithmException e) { + logger.error("Unknown algorithm for PKCE digest", e); + } + } + } + OAuth2AccessTokenEntity token = new OAuth2AccessTokenEntity();//accessTokenFactory.createNewAccessToken(); // attach the client @@ -185,7 +223,7 @@ public OAuth2AccessTokenEntity createAccessToken(OAuth2Authentication authentica // inherit the scope from the auth, but make a new set so it is //not unmodifiable. Unmodifiables don't play nicely with Eclipselink, which //wants to use the clone operation. - Set scopes = scopeService.fromStrings(clientAuth.getScope()); + Set scopes = scopeService.fromStrings(request.getScope()); // remove any of the special system scopes scopes = scopeService.removeReservedScopes(scopes); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectOAuth2RequestFactory.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectOAuth2RequestFactory.java index e5c97c3560..7d697e0786 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectOAuth2RequestFactory.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectOAuth2RequestFactory.java @@ -17,7 +17,7 @@ package org.mitre.openid.connect.request; -import static org.mitre.openid.connect.request.ConnectRequestParameters.AUD; +import static org.mitre.openid.connect.request.ConnectRequestParameters.*; import static org.mitre.openid.connect.request.ConnectRequestParameters.CLAIMS; import static org.mitre.openid.connect.request.ConnectRequestParameters.CLIENT_ID; import static org.mitre.openid.connect.request.ConnectRequestParameters.DISPLAY; @@ -41,6 +41,7 @@ import org.mitre.jwt.signer.service.JWTSigningAndValidationService; import org.mitre.jwt.signer.service.impl.ClientKeyCacheService; import org.mitre.oauth2.model.ClientDetailsEntity; +import org.mitre.oauth2.model.PKCEAlgorithm; import org.mitre.oauth2.service.ClientDetailsEntityService; import org.mitre.oauth2.service.SystemScopeService; import org.slf4j.Logger; @@ -138,6 +139,16 @@ public AuthorizationRequest createAuthorizationRequest(Map input request.getExtensions().put(AUD, inputParams.get(AUD)); } + if (inputParams.containsKey(CODE_CHALLENGE)) { + request.getExtensions().put(CODE_CHALLENGE, inputParams.get(CODE_CHALLENGE)); + if (inputParams.containsKey(CODE_CHALLENGE_METHOD)) { + request.getExtensions().put(CODE_CHALLENGE_METHOD, inputParams.get(CODE_CHALLENGE_METHOD)); + } else { + // if the client doesn't specify a code challenge transformation method, it's "plain" + request.getExtensions().put(CODE_CHALLENGE_METHOD, PKCEAlgorithm.plain.getName()); + } + + } if (inputParams.containsKey(REQUEST)) { request.getExtensions().put(REQUEST, inputParams.get(REQUEST)); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectRequestParameters.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectRequestParameters.java index f0858423aa..3b71edb638 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectRequestParameters.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectRequestParameters.java @@ -46,5 +46,10 @@ public interface ConnectRequestParameters { // audience public String AUD = "aud"; - + + // PKCE + public String CODE_CHALLENGE = "code_challenge"; + public String CODE_CHALLENGE_METHOD = "code_challenge_method"; + public String CODE_VERIFIER = "code_verifier"; + } From ba0d0aab0bc54a4898260b215d855c967f4e0174 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sun, 24 Jul 2016 17:46:04 -0400 Subject: [PATCH 059/269] use parameter constants for extensions maps in token service --- .../connect/service/impl/DefaultOIDCTokenService.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultOIDCTokenService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultOIDCTokenService.java index 5d20338d72..1ec1fa914a 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultOIDCTokenService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultOIDCTokenService.java @@ -16,6 +16,9 @@ *******************************************************************************/ package org.mitre.openid.connect.service.impl; +import static org.mitre.openid.connect.request.ConnectRequestParameters.MAX_AGE; +import static org.mitre.openid.connect.request.ConnectRequestParameters.NONCE; + import java.util.Date; import java.util.Map; import java.util.Set; @@ -58,7 +61,6 @@ import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.PlainJWT; import com.nimbusds.jwt.SignedJWT; - /** * Default implementation of service to create specialty OpenID Connect tokens. * @@ -105,7 +107,7 @@ public OAuth2AccessTokenEntity createIdToken(ClientDetailsEntity client, OAuth2R JWTClaimsSet.Builder idClaims = new JWTClaimsSet.Builder(); // if the auth time claim was explicitly requested OR if the client always wants the auth time, put it in - if (request.getExtensions().containsKey("max_age") + if (request.getExtensions().containsKey(MAX_AGE) || (request.getExtensions().containsKey("idtoken")) // TODO: parse the ID Token claims (#473) -- for now assume it could be in there || (client.getRequireAuthTime() != null && client.getRequireAuthTime())) { @@ -134,7 +136,7 @@ public OAuth2AccessTokenEntity createIdToken(ClientDetailsEntity client, OAuth2R idClaims.audience(Lists.newArrayList(client.getClientId())); idClaims.jwtID(UUID.randomUUID().toString()); // set a random NONCE in the middle of it - String nonce = (String)request.getExtensions().get("nonce"); + String nonce = (String)request.getExtensions().get(NONCE); if (!Strings.isNullOrEmpty(nonce)) { idClaims.claim("nonce", nonce); } From 82c313f0362bba91b5f09be114e1011cae7f071e Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 27 Jul 2016 20:31:14 -0400 Subject: [PATCH 060/269] added PKCE support to client --- .../client/OIDCAuthenticationFilter.java | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java index bbb1b1c018..4c27b0a72c 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java @@ -23,6 +23,9 @@ import java.io.IOException; import java.math.BigInteger; import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.text.ParseException; import java.util.Date; @@ -40,6 +43,7 @@ import org.mitre.jwt.signer.service.JWTSigningAndValidationService; import org.mitre.jwt.signer.service.impl.JWKSetCacheService; import org.mitre.jwt.signer.service.impl.SymmetricKeyJWTValidatorCacheService; +import org.mitre.oauth2.model.PKCEAlgorithm; import org.mitre.oauth2.model.RegisteredClient; import org.mitre.openid.connect.client.model.IssuerServiceResponse; import org.mitre.openid.connect.client.service.AuthRequestOptionsService; @@ -75,6 +79,7 @@ import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSHeader; import com.nimbusds.jose.util.Base64; +import com.nimbusds.jose.util.Base64URL; import com.nimbusds.jwt.JWT; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.JWTParser; @@ -90,10 +95,11 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFilter { protected final static String REDIRECT_URI_SESION_VARIABLE = "redirect_uri"; + protected final static String CODE_VERIFIER_SESSION_VARIABLE = "code_verifier"; protected final static String STATE_SESSION_VARIABLE = "state"; protected final static String NONCE_SESSION_VARIABLE = "nonce"; protected final static String ISSUER_SESSION_VARIABLE = "issuer"; - protected static final String TARGET_SESSION_VARIABLE = "target"; + protected final static String TARGET_SESSION_VARIABLE = "target"; protected final static int HTTP_SOCKET_TIMEOUT = 30000; public final static String FILTER_PROCESSES_URL = "/openid_connect_login"; @@ -262,6 +268,26 @@ protected void handleAuthorizationRequest(HttpServletRequest request, HttpServle String state = createState(session); Map options = authOptions.getOptions(serverConfig, clientConfig, request); + + // if we're using PKCE, handle the challenge here + if (clientConfig.getCodeChallengeMethod() != null) { + String codeVerifier = createCodeVerifier(session); + options.put("code_challenge_method", clientConfig.getCodeChallengeMethod().getName()); + if (clientConfig.getCodeChallengeMethod().equals(PKCEAlgorithm.plain)) { + options.put("code_challenge", codeVerifier); + } else if (clientConfig.getCodeChallengeMethod().equals(PKCEAlgorithm.S256)) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + String hash = Base64URL.encode(digest.digest(codeVerifier.getBytes(StandardCharsets.US_ASCII))).toString(); + options.put("code_challenge", hash); + } catch (NoSuchAlgorithmException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + + } + } String authRequest = authRequestBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options, issResp.getLoginHint()); @@ -302,6 +328,11 @@ protected Authentication handleAuthorizationCodeResponse(HttpServletRequest requ form.add("grant_type", "authorization_code"); form.add("code", authorizationCode); form.setAll(authOptions.getTokenOptions(serverConfig, clientConfig, request)); + + String codeVerifier = getStoredCodeVerifier(session); + if (codeVerifier != null) { + form.add("code_verifier", codeVerifier); + } String redirectUri = getStoredSessionString(session, REDIRECT_URI_SESION_VARIABLE); if (redirectUri != null) { @@ -675,6 +706,26 @@ protected static String createState(HttpSession session) { protected static String getStoredState(HttpSession session) { return getStoredSessionString(session, STATE_SESSION_VARIABLE); } + + /** + * Create a random code challenge and store it in the session + * @param session + * @return + */ + protected static String createCodeVerifier(HttpSession session) { + String challenge = new BigInteger(50, new SecureRandom()).toString(16); + session.setAttribute(CODE_VERIFIER_SESSION_VARIABLE, challenge); + return challenge; + } + + /** + * Retrieve the stored challenge from our session + * @param session + * @return + */ + protected static String getStoredCodeVerifier(HttpSession session) { + return getStoredSessionString(session, CODE_VERIFIER_SESSION_VARIABLE); + } @Override From af7c1f7d45f405a63d06e8ba1b636d78befa77ba Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 27 Jul 2016 20:31:27 -0400 Subject: [PATCH 061/269] added PKCE support to discovery endpoint --- .../main/java/org/mitre/discovery/web/DiscoveryEndpoint.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java b/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java index 0c10d54df8..d89ba20e97 100644 --- a/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java @@ -24,6 +24,7 @@ import org.mitre.discovery.util.WebfingerURLNormalizer; import org.mitre.jwt.encryption.service.JWTEncryptionAndDecryptionService; import org.mitre.jwt.signer.service.JWTSigningAndValidationService; +import org.mitre.oauth2.model.PKCEAlgorithm; import org.mitre.oauth2.service.SystemScopeService; import org.mitre.oauth2.web.IntrospectionEndpoint; import org.mitre.oauth2.web.RevocationEndpoint; @@ -364,6 +365,9 @@ OPTIONAL. JSON array containing a list of the JWS signing algorithms (alg values m.put("introspection_endpoint", baseUrl + IntrospectionEndpoint.URL); // token introspection endpoint for verifying tokens m.put("revocation_endpoint", baseUrl + RevocationEndpoint.URL); // token revocation endpoint + m.put("code_challenge_methods_supported", Lists.newArrayList(PKCEAlgorithm.plain.getName(), PKCEAlgorithm.S256.getName())); + + model.addAttribute(JsonEntityView.ENTITY, m); return JsonEntityView.VIEWNAME; From 74f3e2d0c0214c0f3d95e50398ffdf44d6ea025f Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 1 Aug 2016 13:36:06 -0400 Subject: [PATCH 062/269] maven site generator working, closes #984 #941 --- pom.xml | 176 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 120 insertions(+), 56 deletions(-) diff --git a/pom.xml b/pom.xml index b75f6c0503..b3420ffc98 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. --> - + 4.0.0 org.mitre openid-connect-parent @@ -72,7 +73,7 @@ 1.8 4.1.1.RELEASE 3.2.5.RELEASE - 1.7.12 + 1.7.21 A reference implementation of OpenID Connect (http://openid.net/connect/), OAuth 2.0, and UMA built on top of Java, Spring, and Spring Security. The project contains a fully functioning server, client, and utility library. https://github.com/mitreid-connect @@ -87,7 +88,7 @@ org.apache.maven.plugins maven-war-plugin - 2.2 + 2.6 org.apache.maven.plugins @@ -130,6 +131,16 @@ warpath-maven-plugin 3.5.0 + + org.apache.maven.plugins + maven-site-plugin + 3.3 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 2.9 + @@ -196,6 +207,96 @@ + + org.apache.maven.plugins + maven-site-plugin + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 2.4 + + false + false + + + index + cim + dependencies + dependency-convergence + + dependency-management + help + issue-tracking + license + mailing-list + modules + plugin-management + plugins + project-team + scm + summary + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9 + + true + true + true + true + MITREid Connect ${project.name} v. ${project.version} + MITREid Connect ${project.name} v. ${project.version} + ${basedir}/src/main/javadoc/overview.html + -Xdoclint:none + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.10 + + checkstyle.xml + + + + org.apache.maven.plugins + maven-surefire-plugin + + junit:junit + + **/*_Roo_* + + + + + org.codehaus.mojo + cobertura-maven-plugin + 2.7 + + + html + xml + + + + + + + + + + + + + + + + + @@ -207,55 +308,6 @@ https://travis-ci.org/mitreid-connect/OpenID-Connect-Java-Spring-Server - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9 - - true - true - true - true - MITREid Connect v. ${project.version} - MITREid Connect v. ${project.version} - ${basedir}/src/main/javadoc/overview.html - - - - org.apache.maven.plugins - maven-checkstyle-plugin - 2.10 - - checkstyle.xml - - - - org.apache.maven.plugins - maven-surefire-plugin - - junit:junit - - **/*_Roo_* - - - - - org.codehaus.mojo - cobertura-maven-plugin - 2.5.2 - - - html - xml - - - - - - - @@ -292,7 +344,7 @@ spring-security-oauth2 2.0.3.RELEASE - + javax.servlet @@ -412,6 +464,13 @@ 1.9.5 test + + org.codehaus.mojo + cobertura-maven-plugin + 2.7 + test + maven-plugin + @@ -481,9 +540,9 @@ 4.3 - org.bouncycastle - bcprov-jdk15on - [1.52,) + org.bouncycastle + bcprov-jdk15on + [1.52,) org.eclipse.persistence @@ -533,6 +592,11 @@ javax.servlet.jsp jsp-api + + org.codehaus.mojo + cobertura-maven-plugin + maven-plugin + From d0056ae88212ed34c5b52ddebd1130c5e96fadf8 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 10 Oct 2016 15:32:32 -0400 Subject: [PATCH 063/269] added target link URI capability to webfinger issuer service --- .../connect/client/service/impl/WebfingerIssuerService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java index d4f9d5ab36..0eedb0ffb7 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java @@ -114,8 +114,8 @@ public IssuerServiceResponse getIssuer(HttpServletRequest request) { if (blacklist.contains(lr.issuer)) { throw new AuthenticationServiceException("Issuer was in blacklist: " + lr.issuer); } - - return new IssuerServiceResponse(lr.issuer, lr.loginHint, null); + + return new IssuerServiceResponse(lr.issuer, lr.loginHint, request.getParameter("target_link_uri")); } catch (UncheckedExecutionException | ExecutionException e) { logger.warn("Issue fetching issuer for user input: " + identifier + ": " + e.getMessage()); return null; From 74d34ab74431ed5997faf46e338b1b875a7d53ee Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 10 Oct 2016 15:10:20 -0400 Subject: [PATCH 064/269] null-safe target link filter --- .../mitre/openid/connect/client/OIDCAuthenticationFilter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java index 4c27b0a72c..c35304aaf6 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java @@ -759,7 +759,9 @@ public void onAuthenticationSuccess(HttpServletRequest request, if (!Strings.isNullOrEmpty(target)) { session.removeAttribute(TARGET_SESSION_VARIABLE); - target = deepLinkFilter.filter(target); + if (deepLinkFilter != null) { + target = deepLinkFilter.filter(target); + } response.sendRedirect(target); } else { From 337513a559b8712b3f17f4501ea46a596fc60096 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 2 Dec 2016 14:28:56 -0500 Subject: [PATCH 065/269] =?UTF-8?q?sync=E2=80=99d=20database=20tables=20fr?= =?UTF-8?q?om=20HSQL=20to=20MySQL=20and=20PGSQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #1154 closes #1148 --- .../src/main/resources/db/tables/mysql_database_tables.sql | 4 ++++ .../src/main/resources/db/tables/psql_database_tables.sql | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/mysql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/tables/mysql_database_tables.sql index ee145bac88..fea38cfc2f 100644 --- a/openid-connect-server-webapp/src/main/resources/db/tables/mysql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/tables/mysql_database_tables.sql @@ -169,6 +169,10 @@ CREATE TABLE IF NOT EXISTS client_details ( initiate_login_uri VARCHAR(2048), clear_access_tokens_on_refresh BOOLEAN DEFAULT true NOT NULL, + software_statement VARCHAR(4096), + + code_challenge_method VARCHAR(256), + UNIQUE (client_id) ); diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/psql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/tables/psql_database_tables.sql index da7e697671..01682b7053 100644 --- a/openid-connect-server-webapp/src/main/resources/db/tables/psql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/tables/psql_database_tables.sql @@ -169,6 +169,10 @@ CREATE TABLE IF NOT EXISTS client_details ( initiate_login_uri VARCHAR(2048), clear_access_tokens_on_refresh BOOLEAN DEFAULT true NOT NULL, + software_statement VARCHAR(4096), + + code_challenge_method VARCHAR(256), + UNIQUE (client_id) ); From 6f5ca3fd2fbce9d3959b487f191a9b115e052186 Mon Sep 17 00:00:00 2001 From: pwolf23 Date: Thu, 11 Aug 2016 08:38:39 +0200 Subject: [PATCH 066/269] Fixed missing "final" modifier in constant --- .../java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java index 00c966cd9b..ba7af670cc 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java @@ -93,7 +93,7 @@ public class OAuth2AccessTokenEntity implements OAuth2AccessToken { public static final String PARAM_DATE = "date"; public static final String PARAM_RESOURCE_SET_ID = "rsid"; - public static String ID_TOKEN_FIELD_NAME = "id_token"; + public static final String ID_TOKEN_FIELD_NAME = "id_token"; private Long id; From cbe6b9e1df32badbe40c909ba19c7f1c8b7bcd9c Mon Sep 17 00:00:00 2001 From: Neths Date: Tue, 13 Sep 2016 15:51:42 +0200 Subject: [PATCH 067/269] add db init script for pgsql add data-context example with pgsql db initializer --- .../src/main/resources/db/psql/clients.sql | 66 +++++++++++++++++++ .../src/main/resources/db/psql/scopes.sql | 33 ++++++++++ .../src/main/resources/db/psql/users.sql | 55 ++++++++++++++++ .../src/main/webapp/WEB-INF/data-context.xml | 9 +++ 4 files changed, 163 insertions(+) create mode 100644 openid-connect-server-webapp/src/main/resources/db/psql/clients.sql create mode 100644 openid-connect-server-webapp/src/main/resources/db/psql/scopes.sql create mode 100644 openid-connect-server-webapp/src/main/resources/db/psql/users.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/psql/clients.sql b/openid-connect-server-webapp/src/main/resources/db/psql/clients.sql new file mode 100644 index 0000000000..bf14c2b2b6 --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/psql/clients.sql @@ -0,0 +1,66 @@ +-- +-- Turn off autocommit and start a transaction so that we can use the temp tables +-- + +--SET AUTOCOMMIT = OFF; + +START TRANSACTION; + +-- +-- Insert client information into the temporary tables. To add clients to the HSQL database, edit things here. +-- + +INSERT INTO client_details_TEMP (client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection) VALUES + ('client', 'secret', 'Test Client', false, null, 3600, 600, true); + +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES + ('client', 'openid'), + ('client', 'profile'), + ('client', 'email'), + ('client', 'address'), + ('client', 'phone'), + ('client', 'offline_access'); + +INSERT INTO client_redirect_uri_TEMP (owner_id, redirect_uri) VALUES + ('client', 'http://localhost/'), + ('client', 'http://localhost:8080/'); + +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES + ('client', 'authorization_code'), + ('client', 'urn:ietf:params:oauth:grant_type:redelegate'), + ('client', 'implicit'), + ('client', 'refresh_token'); + +-- +-- Merge the temporary clients safely into the database. This is a two-step process to keep clients from being created on every startup with a persistent store. +-- + +INSERT INTO client_details (client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection) + SELECT client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection FROM client_details_TEMP + ON CONFLICT + DO NOTHING; + +INSERT INTO client_scope (scope) + SELECT scope FROM client_scope_TEMP, client_details WHERE client_details.client_id = client_scope_TEMP.owner_id + ON CONFLICT + DO NOTHING; + +INSERT INTO client_redirect_uri (redirect_uri) + SELECT redirect_uri FROM client_redirect_uri_TEMP, client_details WHERE client_details.client_id = client_redirect_uri_TEMP.owner_id + ON CONFLICT + DO NOTHING; + +INSERT INTO client_grant_type (grant_type) + SELECT grant_type FROM client_grant_type_TEMP, client_details WHERE client_details.client_id = client_grant_type_TEMP.owner_id + ON CONFLICT + DO NOTHING; + +-- +-- Close the transaction and turn autocommit back on +-- + +COMMIT; + +--SET AUTOCOMMIT = ON; + + diff --git a/openid-connect-server-webapp/src/main/resources/db/psql/scopes.sql b/openid-connect-server-webapp/src/main/resources/db/psql/scopes.sql new file mode 100644 index 0000000000..8b2611b832 --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/psql/scopes.sql @@ -0,0 +1,33 @@ +-- +-- Turn off autocommit and start a transaction so that we can use the temp tables +-- + +--SET AUTOCOMMIT = OFF; + +START TRANSACTION; + +-- +-- Insert scope information into the temporary tables. +-- + +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('openid', 'log in using your identity', 'user', false, true, false, null), + ('profile', 'basic profile information', 'list-alt', false, true, false, null), + ('email', 'email address', 'envelope', false, true, false, null), + ('address', 'physical address', 'home', false, true, false, null), + ('phone', 'telephone number', 'bell', false, true, false, null), + ('offline_access', 'offline access', 'time', false, false, false, null); + +-- +-- Merge the temporary scopes safely into the database. This is a two-step process to keep scopes from being created on every startup with a persistent store. +-- + +INSERT INTO system_scope (scope, description, icon, restricted, default_scope, structured, structured_param_description) + SELECT scope, description, icon, restricted, default_scope, structured, structured_param_description FROM system_scope_TEMP + ON CONFLICT(scope) + DO NOTHING; + +COMMIT; + +--SET AUTOCOMMIT = ON; + diff --git a/openid-connect-server-webapp/src/main/resources/db/psql/users.sql b/openid-connect-server-webapp/src/main/resources/db/psql/users.sql new file mode 100644 index 0000000000..537330278c --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/psql/users.sql @@ -0,0 +1,55 @@ +-- +-- Turn off autocommit and start a transaction so that we can use the temp tables +-- + +--SET AUTOCOMMIT FALSE; + +START TRANSACTION; + +-- +-- Insert user information into the temporary tables. To add users to the HSQL database, edit things here. +-- + +INSERT INTO users_TEMP (username, password, enabled) VALUES + ('admin','password',true), + ('user','password',true); + + +INSERT INTO authorities_TEMP (username, authority) VALUES + ('admin','ROLE_ADMIN'), + ('admin','ROLE_USER'), + ('user','ROLE_USER'); + +-- By default, the username column here has to match the username column in the users table, above +INSERT INTO user_info_TEMP (sub, preferred_username, name, email, email_verified) VALUES + ('90342.ASDFJWFA','admin','Demo Admin','admin@example.com', true), + ('01921.FLANRJQW','user','Demo User','user@example.com', true); + + +-- +-- Merge the temporary users safely into the database. This is a two-step process to keep users from being created on every startup with a persistent store. +-- + +INSERT INTO users + SELECT username, password, enabled FROM users_TEMP + ON CONFLICT(username) + DO NOTHING; + +INSERT INTO authorities + SELECT username, authority FROM authorities_TEMP + ON CONFLICT(username, authority) + DO NOTHING; + +INSERT INTO user_info (sub, preferred_username, name, email, email_verified) + SELECT sub, preferred_username, name, email, email_verified FROM user_info_TEMP + ON CONFLICT + DO NOTHING; + +-- +-- Close the transaction and turn autocommit back on +-- + +COMMIT; + +--SET AUTOCOMMIT TRUE; + diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml index 484e71ac48..6b8dd8e155 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml @@ -74,6 +74,15 @@ + + + + + + + + + From 1b7612a26d31a0119c4c4d26f54e87297aa45a93 Mon Sep 17 00:00:00 2001 From: Neths Date: Wed, 14 Sep 2016 16:18:26 +0200 Subject: [PATCH 068/269] add db init script for mysql add data-context example with mysql db initializer --- .../src/main/resources/db/mysql/clients.sql | 61 +++++++++++++++++++ .../src/main/resources/db/mysql/scopes.sql | 31 ++++++++++ .../src/main/resources/db/mysql/users.sql | 52 ++++++++++++++++ .../src/main/webapp/WEB-INF/data-context.xml | 22 ++++--- 4 files changed, 158 insertions(+), 8 deletions(-) create mode 100644 openid-connect-server-webapp/src/main/resources/db/mysql/clients.sql create mode 100644 openid-connect-server-webapp/src/main/resources/db/mysql/scopes.sql create mode 100644 openid-connect-server-webapp/src/main/resources/db/mysql/users.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/mysql/clients.sql b/openid-connect-server-webapp/src/main/resources/db/mysql/clients.sql new file mode 100644 index 0000000000..7f02557899 --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/mysql/clients.sql @@ -0,0 +1,61 @@ +-- +-- Turn off autocommit and start a transaction so that we can use the temp tables +-- + +SET AUTOCOMMIT = 0; + +START TRANSACTION; + +-- +-- Insert client information into the temporary tables. To add clients to the HSQL database, edit things here. +-- + +INSERT INTO client_details_TEMP (client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection) VALUES + ('client', 'secret', 'Test Client', false, null, 3600, 600, true); + +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES + ('client', 'openid'), + ('client', 'profile'), + ('client', 'email'), + ('client', 'address'), + ('client', 'phone'), + ('client', 'offline_access'); + +INSERT INTO client_redirect_uri_TEMP (owner_id, redirect_uri) VALUES + ('client', 'http://localhost/'), + ('client', 'http://localhost:8080/'); + +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES + ('client', 'authorization_code'), + ('client', 'urn:ietf:params:oauth:grant_type:redelegate'), + ('client', 'implicit'), + ('client', 'refresh_token'); + +-- +-- Merge the temporary clients safely into the database. This is a two-step process to keep clients from being created on every startup with a persistent store. +-- + +INSERT INTO client_details (client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection) + SELECT client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection FROM client_details_TEMP + ON DUPLICATE KEY UPDATE client_details.client_id = client_details.client_id; + +INSERT INTO client_scope (owner_id, scope) + SELECT id, scope FROM client_scope_TEMP, client_details WHERE client_details.client_id = client_scope_TEMP.owner_id + ON DUPLICATE KEY UPDATE client_scope.owner_id = client_scope.owner_id; + +INSERT INTO client_redirect_uri (owner_id, redirect_uri) + SELECT id, redirect_uri FROM client_redirect_uri_TEMP, client_details WHERE client_details.client_id = client_redirect_uri_TEMP.owner_id + ON DUPLICATE KEY UPDATE client_redirect_uri.owner_id = client_redirect_uri.owner_id; + +INSERT INTO client_grant_type (owner_id, grant_type) + SELECT id, grant_type FROM client_grant_type_TEMP, client_details WHERE client_details.client_id = client_grant_type_TEMP.owner_id + ON DUPLICATE KEY UPDATE client_grant_type.owner_id = client_grant_type.owner_id; + +-- +-- Close the transaction and turn autocommit back on +-- + +COMMIT; + +SET AUTOCOMMIT = 1; + diff --git a/openid-connect-server-webapp/src/main/resources/db/mysql/scopes.sql b/openid-connect-server-webapp/src/main/resources/db/mysql/scopes.sql new file mode 100644 index 0000000000..62d5dcd29a --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/mysql/scopes.sql @@ -0,0 +1,31 @@ +-- +-- Turn off autocommit and start a transaction so that we can use the temp tables +-- + +SET AUTOCOMMIT = 0; + +START TRANSACTION; + +-- +-- Insert scope information into the temporary tables. +-- + +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('openid', 'log in using your identity', 'user', false, true, false, null), + ('profile', 'basic profile information', 'list-alt', false, true, false, null), + ('email', 'email address', 'envelope', false, true, false, null), + ('address', 'physical address', 'home', false, true, false, null), + ('phone', 'telephone number', 'bell', false, true, false, null), + ('offline_access', 'offline access', 'time', false, false, false, null); + +-- +-- Merge the temporary scopes safely into the database. This is a two-step process to keep scopes from being created on every startup with a persistent store. +-- + +INSERT INTO system_scope (scope, description, icon, restricted, default_scope, structured, structured_param_description) + SELECT scope, description, icon, restricted, default_scope, structured, structured_param_description FROM system_scope_TEMP + ON DUPLICATE KEY UPDATE system_scope.scope = system_scope.scope; + +COMMIT; + +SET AUTOCOMMIT = 1; \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/resources/db/mysql/users.sql b/openid-connect-server-webapp/src/main/resources/db/mysql/users.sql new file mode 100644 index 0000000000..fc82e48006 --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/mysql/users.sql @@ -0,0 +1,52 @@ +-- +-- Turn off autocommit and start a transaction so that we can use the temp tables +-- + +SET AUTOCOMMIT = 0; + +START TRANSACTION; + +-- +-- Insert user information into the temporary tables. To add users to the HSQL database, edit things here. +-- + +INSERT INTO users_TEMP (username, password, enabled) VALUES + ('admin','password',true), + ('user','password',true); + + +INSERT INTO authorities_TEMP (username, authority) VALUES + ('admin','ROLE_ADMIN'), + ('admin','ROLE_USER'), + ('user','ROLE_USER'); + +-- By default, the username column here has to match the username column in the users table, above +INSERT INTO user_info_TEMP (sub, preferred_username, name, email, email_verified) VALUES + ('90342.ASDFJWFA','admin','Demo Admin','admin@example.com', true), + ('01921.FLANRJQW','user','Demo User','user@example.com', true); + + +-- +-- Merge the temporary users safely into the database. This is a two-step process to keep users from being created on every startup with a persistent store. +-- + +INSERT INTO users (username, password, enabled) + SELECT username, password, enabled FROM users_TEMP + ON DUPLICATE KEY UPDATE users.username = users.username; + +INSERT INTO authorities (username,authority) + SELECT username, authority FROM authorities_TEMP + ON DUPLICATE KEY UPDATE authorities.username = authorities.username; + +INSERT INTO user_info (sub, preferred_username, name, email, email_verified) + SELECT sub, preferred_username, name, email, email_verified FROM user_info_TEMP + ON DUPLICATE KEY UPDATE user_info.preferred_username = user_info.preferred_username; + +-- +-- Close the transaction and turn autocommit back on +-- + +COMMIT; + +SET AUTOCOMMIT = 1; + diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml index 6b8dd8e155..41b6c86885 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml @@ -49,20 +49,26 @@ - + --> From dea6044e77d219cb6e94aa1d2fe5375f2a47da5b Mon Sep 17 00:00:00 2001 From: Nicolas Liampotis Date: Tue, 6 Sep 2016 02:43:33 +0300 Subject: [PATCH 069/269] Set the encoding of the UserInfo response body to UTF-8 See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse --- .../main/java/org/mitre/openid/connect/view/UserInfoView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java index d6d27b9c41..b774e9a443 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java @@ -99,6 +99,7 @@ protected void renderMergedOutputModel(Map model, HttpServletReq Set scope = (Set) model.get(SCOPE); response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding("UTF-8"); JsonObject authorizedClaims = null; From 83a9fef14dd4d29ca456c87f3ed2147a96e71e30 Mon Sep 17 00:00:00 2001 From: Tomasz Borowiec Date: Thu, 3 Nov 2016 16:01:18 +0100 Subject: [PATCH 070/269] Oracle support added --- openid-connect-server-webapp/pom.xml | 7 + .../src/main/resources/db/clients_oracle.sql | 51 +++ .../src/main/resources/db/scopes_oracle.sql | 27 ++ .../db/tables/loading_temp_tables_oracle.sql | 77 ++++ .../db/tables/oracle_database_tables.sql | 387 ++++++++++++++++++ .../db/tables/security-schema_oracle.sql | 16 + .../src/main/resources/db/users_oracle.sql | 39 ++ .../main/resources/entity-mappings_oracle.xml | 266 ++++++++++++ .../src/main/webapp/WEB-INF/data-context.xml | 25 ++ .../src/main/webapp/WEB-INF/jpa-config.xml | 2 + 10 files changed, 897 insertions(+) create mode 100644 openid-connect-server-webapp/src/main/resources/db/clients_oracle.sql create mode 100644 openid-connect-server-webapp/src/main/resources/db/scopes_oracle.sql create mode 100644 openid-connect-server-webapp/src/main/resources/db/tables/loading_temp_tables_oracle.sql create mode 100644 openid-connect-server-webapp/src/main/resources/db/tables/oracle_database_tables.sql create mode 100644 openid-connect-server-webapp/src/main/resources/db/tables/security-schema_oracle.sql create mode 100644 openid-connect-server-webapp/src/main/resources/db/users_oracle.sql create mode 100644 openid-connect-server-webapp/src/main/resources/entity-mappings_oracle.xml diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index 4f35ad1c57..87b645b77d 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -133,6 +133,13 @@ com.zaxxer HikariCP + + + Deployable package of the OpenID Connect server diff --git a/openid-connect-server-webapp/src/main/resources/db/clients_oracle.sql b/openid-connect-server-webapp/src/main/resources/db/clients_oracle.sql new file mode 100644 index 0000000000..488d928457 --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/clients_oracle.sql @@ -0,0 +1,51 @@ +-- +-- Insert client information into the temporary tables. To add clients to the Oracle database, edit things here. +-- + +INSERT INTO client_details_TEMP (client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection) VALUES + ('client', 'secret', 'Test Client', 0, null, 3600, 600, 1); + +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES ('client', 'openid'); +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES ('client', 'profile'); +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES ('client', 'email'); +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES ('client', 'address'); +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES ('client', 'phone'); +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES ('client', 'offline_access'); + +INSERT INTO client_redirect_uri_TEMP (owner_id, redirect_uri) VALUES ('client', 'http://localhost/'); +INSERT INTO client_redirect_uri_TEMP (owner_id, redirect_uri) VALUES ('client', 'http://localhost:8080/'); + +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES ('client', 'authorization_code'); +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES ('client', 'urn:ietf:params:oauth:grant_type:redelegate'); +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES ('client', 'implicit'); +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES ('client', 'refresh_token'); + +-- +-- Merge the temporary clients safely into the database. This is a two-step process to keep clients from being created on every startup with a persistent store. +-- + +MERGE INTO client_details + USING (SELECT client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection FROM client_details_TEMP) vals + ON (vals.client_id = client_details.client_id) + WHEN NOT MATCHED THEN + INSERT (id, client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, + id_token_validity_seconds, allow_introspection) VALUES(client_details_seq.nextval, vals.client_id, vals.client_secret, vals.client_name, vals.dynamically_registered, + vals.refresh_token_validity_seconds, vals.access_token_validity_seconds, vals.id_token_validity_seconds, vals.allow_introspection); + +MERGE INTO client_scope + USING (SELECT id, scope FROM client_scope_TEMP, client_details WHERE client_details.client_id = client_scope_TEMP.owner_id) vals + ON (vals.id = client_scope.owner_id AND vals.scope = client_scope.scope) + WHEN NOT MATCHED THEN + INSERT (owner_id, scope) values (vals.id, vals.scope); + +MERGE INTO client_redirect_uri + USING (SELECT id, redirect_uri FROM client_redirect_uri_TEMP, client_details WHERE client_details.client_id = client_redirect_uri_TEMP.owner_id) vals + ON (vals.id = client_redirect_uri.owner_id AND vals.redirect_uri = client_redirect_uri.redirect_uri) + WHEN NOT MATCHED THEN + INSERT (owner_id, redirect_uri) values (vals.id, vals.redirect_uri); + +MERGE INTO client_grant_type + USING (SELECT id, grant_type FROM client_grant_type_TEMP, client_details WHERE client_details.client_id = client_grant_type_TEMP.owner_id) vals + ON (vals.id = client_grant_type.owner_id AND vals.grant_type = client_grant_type.grant_type) + WHEN NOT MATCHED THEN + INSERT (owner_id, grant_type) values (vals.id, vals.grant_type); diff --git a/openid-connect-server-webapp/src/main/resources/db/scopes_oracle.sql b/openid-connect-server-webapp/src/main/resources/db/scopes_oracle.sql new file mode 100644 index 0000000000..98e98bfcbe --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/scopes_oracle.sql @@ -0,0 +1,27 @@ +-- +-- Insert scope information into the temporary tables. +-- + +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('openid', 'log in using your identity', 'user', 0, 1, 0, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('profile', 'basic profile information', 'list-alt', 0, 1, 0, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('email', 'email address', 'envelope', 0, 1, 0, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('address', 'physical address', 'home', 0, 1, 0, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('phone', 'telephone number', 'bell', 0, 1, 0, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('offline_access', 'offline access', 'time', 0, 0, 0, null); + +-- +-- Merge the temporary scopes safely into the database. This is a two-step process to keep scopes from being created on every startup with a persistent store. +-- + +MERGE INTO system_scope + USING (SELECT scope, description, icon, restricted, default_scope, structured, structured_param_description FROM system_scope_TEMP) vals + ON (vals.scope = system_scope.scope) + WHEN NOT MATCHED THEN + INSERT (id, scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES(system_scope_seq.nextval, vals.scope, + vals.description, vals.icon, vals.restricted, vals.default_scope, vals.structured, vals.structured_param_description); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/loading_temp_tables_oracle.sql b/openid-connect-server-webapp/src/main/resources/db/tables/loading_temp_tables_oracle.sql new file mode 100644 index 0000000000..f91f3e633c --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/tables/loading_temp_tables_oracle.sql @@ -0,0 +1,77 @@ +-- +-- Temporary tables used during the bootstrapping process to safely load users and clients. +-- These are not needed if you're not using the users.sql/clients.sql files to bootstrap the database. +-- + +CREATE GLOBAL TEMPORARY TABLE authorities_TEMP ( + username varchar2(50) not null, + authority varchar2(50) not null, + constraint ix_authority_TEMP unique (username,authority) +) ON COMMIT PRESERVE ROWS; + +CREATE GLOBAL TEMPORARY TABLE users_TEMP ( + username VARCHAR2(50) not null primary key, + password VARCHAR2(50) not null, + enabled NUMBER(1) not null +) ON COMMIT PRESERVE ROWS; + +CREATE GLOBAL TEMPORARY TABLE user_info_TEMP ( + sub VARCHAR2(256) not null primary key, + preferred_username VARCHAR2(256), + name VARCHAR2(256), + given_name VARCHAR2(256), + family_name VARCHAR2(256), + middle_name VARCHAR2(256), + nickname VARCHAR2(256), + profile VARCHAR2(256), + picture VARCHAR2(256), + website VARCHAR2(256), + email VARCHAR2(256), + email_verified NUMBER(1), + gender VARCHAR2(256), + zone_info VARCHAR2(256), + locale VARCHAR2(256), + phone_number VARCHAR2(256), + address_id VARCHAR2(256), + updated_time VARCHAR2(256), + birthdate VARCHAR2(256) +) ON COMMIT PRESERVE ROWS; + +CREATE GLOBAL TEMPORARY TABLE client_details_TEMP ( + client_description VARCHAR2(256), + dynamically_registered NUMBER(1), + id_token_validity_seconds NUMBER(19), + + client_id VARCHAR2(256), + client_secret VARCHAR2(2048), + access_token_validity_seconds NUMBER(19), + refresh_token_validity_seconds NUMBER(19), + allow_introspection NUMBER(1), + + client_name VARCHAR2(256) +) ON COMMIT PRESERVE ROWS; + +CREATE GLOBAL TEMPORARY TABLE client_scope_TEMP ( + owner_id VARCHAR2(256), + scope VARCHAR2(2048) +) ON COMMIT PRESERVE ROWS; + +CREATE GLOBAL TEMPORARY TABLE client_redirect_uri_TEMP ( + owner_id VARCHAR2(256), + redirect_uri VARCHAR2(2048) +) ON COMMIT PRESERVE ROWS; + +CREATE GLOBAL TEMPORARY TABLE client_grant_type_TEMP ( + owner_id VARCHAR2(256), + grant_type VARCHAR2(2000) +) ON COMMIT PRESERVE ROWS; + +CREATE GLOBAL TEMPORARY TABLE system_scope_TEMP ( + scope VARCHAR2(256), + description VARCHAR2(4000), + icon VARCHAR2(256), + restricted NUMBER(1), + default_scope NUMBER(1), + structured NUMBER(1), + structured_param_description VARCHAR2(256) +) ON COMMIT PRESERVE ROWS; \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/oracle_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/tables/oracle_database_tables.sql new file mode 100644 index 0000000000..106369ed45 --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/tables/oracle_database_tables.sql @@ -0,0 +1,387 @@ +-- +-- Tables for OIDC Server functionality, Oracle +-- + +CREATE TABLE access_token ( + id NUMBER(19) NOT NULL PRIMARY KEY, + token_value VARCHAR2(4000), + expiration TIMESTAMP, + token_type VARCHAR2(256), + refresh_token_id NUMBER(19), + client_id NUMBER(19), + auth_holder_id NUMBER(19), + id_token_id NUMBER(19), + approved_site_id NUMBER(19) +); +CREATE SEQUENCE access_token_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE access_token_permissions ( + access_token_id NUMBER(19) NOT NULL, + permission_id NUMBER(19) NOT NULL +); + +CREATE TABLE address ( + id NUMBER(19) NOT NULL PRIMARY KEY, + formatted VARCHAR2(256), + street_address VARCHAR2(256), + locality VARCHAR2(256), + region VARCHAR2(256), + postal_code VARCHAR2(256), + country VARCHAR2(256) +); +CREATE SEQUENCE address_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE approved_site ( + id NUMBER(19) NOT NULL PRIMARY KEY, + user_id VARCHAR2(256), + client_id VARCHAR2(256), + creation_date TIMESTAMP, + access_date TIMESTAMP, + timeout_date TIMESTAMP, + whitelisted_site_id NUMBER(19) +); +CREATE SEQUENCE approved_site_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE approved_site_scope ( + owner_id NUMBER(19), + scope VARCHAR2(256) +); + +CREATE TABLE authentication_holder ( + id NUMBER(19) NOT NULL PRIMARY KEY, + user_auth_id NUMBER(19), + approved NUMBER(1), + redirect_uri VARCHAR2(2048), + client_id VARCHAR2(256) +); +CREATE SEQUENCE authentication_holder_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE auth_holder_authority ( + owner_id NUMBER(19), + authority VARCHAR2(256) +); + +CREATE TABLE auth_holder_resource_id ( + owner_id NUMBER(19), + resource_id VARCHAR2(2048) +); + +CREATE TABLE auth_holder_response_type ( + owner_id NUMBER(19), + response_type VARCHAR2(2048) +); + +CREATE TABLE auth_holder_extension ( + owner_id NUMBER(19), + extension VARCHAR2(2048), + val VARCHAR2(2048) +); + +CREATE TABLE authentication_holder_scope ( + owner_id NUMBER(19), + scope VARCHAR2(2048) +); + +CREATE TABLE auth_holder_request_parameter ( + owner_id NUMBER(19), + param VARCHAR2(2048), + val VARCHAR2(2048) +); + +CREATE TABLE saved_user_auth ( + id NUMBER(19) NOT NULL PRIMARY KEY, + name VARCHAR2(1024), + authenticated NUMBER(1), + source_class VARCHAR2(2048) +); +CREATE SEQUENCE saved_user_auth_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE saved_user_auth_authority ( + owner_id NUMBER(19), + authority VARCHAR2(256) +); + +CREATE TABLE client_authority ( + owner_id NUMBER(19), + authority VARCHAR2(256) +); + +CREATE TABLE authorization_code ( + id NUMBER(19) NOT NULL PRIMARY KEY, + code VARCHAR2(256), + auth_holder_id NUMBER(19), + expiration TIMESTAMP +); +CREATE SEQUENCE authorization_code_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE client_grant_type ( + owner_id NUMBER(19), + grant_type VARCHAR2(2000) +); + +CREATE TABLE client_response_type ( + owner_id NUMBER(19), + response_type VARCHAR2(2000) +); + +CREATE TABLE blacklisted_site ( + id NUMBER(19) NOT NULL PRIMARY KEY, + uri VARCHAR2(2048) +); +CREATE SEQUENCE blacklisted_site_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE client_details ( + id NUMBER(19) NOT NULL PRIMARY KEY, + + client_description VARCHAR2(1024), + reuse_refresh_tokens NUMBER(1) DEFAULT 1 NOT NULL, + dynamically_registered NUMBER(1) DEFAULT 0 NOT NULL, + allow_introspection NUMBER(1) DEFAULT 0 NOT NULL, + id_token_validity_seconds NUMBER(19) DEFAULT 600 NOT NULL, + + client_id VARCHAR2(256), + client_secret VARCHAR2(2048), + access_token_validity_seconds NUMBER(19), + refresh_token_validity_seconds NUMBER(19), + + application_type VARCHAR2(256), + client_name VARCHAR2(256), + token_endpoint_auth_method VARCHAR2(256), + subject_type VARCHAR2(256), + + logo_uri VARCHAR2(2048), + policy_uri VARCHAR2(2048), + client_uri VARCHAR2(2048), + tos_uri VARCHAR2(2048), + + jwks_uri VARCHAR2(2048), + jwks CLOB, + sector_identifier_uri VARCHAR2(2048), + + request_object_signing_alg VARCHAR2(256), + + user_info_signed_response_alg VARCHAR2(256), + user_info_encrypted_resp_alg VARCHAR2(256), + user_info_encrypted_resp_enc VARCHAR2(256), + + id_token_signed_response_alg VARCHAR2(256), + id_token_encrypted_resp_alg VARCHAR2(256), + id_token_encrypted_resp_enc VARCHAR2(256), + + token_endpoint_auth_sign_alg VARCHAR2(256), + + default_max_age NUMBER(19), + require_auth_time NUMBER(1), + created_at TIMESTAMP, + initiate_login_uri VARCHAR2(2048), + clear_access_tokens_on_refresh NUMBER(1) DEFAULT 1 NOT NULL, + + software_statement VARCHAR2(4000), + + code_challenge_method VARCHAR2(256), + + CONSTRAINT client_details_unique UNIQUE (client_id) +); +CREATE SEQUENCE client_details_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE client_request_uri ( + owner_id NUMBER(19), + request_uri VARCHAR2(2000) +); + +CREATE TABLE client_post_logout_redir_uri ( + owner_id NUMBER(19), + post_logout_redirect_uri VARCHAR2(2000) +); + +CREATE TABLE client_default_acr_value ( + owner_id NUMBER(19), + default_acr_value VARCHAR2(2000) +); + +CREATE TABLE client_contact ( + owner_id NUMBER(19), + contact VARCHAR2(256) +); + +CREATE TABLE client_redirect_uri ( + owner_id NUMBER(19), + redirect_uri VARCHAR2(2048) +); + +CREATE TABLE client_claims_redirect_uri ( + owner_id NUMBER(19), + redirect_uri VARCHAR2(2048) +); + +CREATE TABLE refresh_token ( + id NUMBER(19) NOT NULL PRIMARY KEY, + token_value VARCHAR2(4000), + expiration TIMESTAMP, + auth_holder_id NUMBER(19), + client_id NUMBER(19) +); +CREATE SEQUENCE refresh_token_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE client_resource ( + owner_id NUMBER(19), + resource_id VARCHAR2(256) +); + +CREATE TABLE client_scope ( + owner_id NUMBER(19), + scope VARCHAR2(2048) +); + +CREATE TABLE token_scope ( + owner_id NUMBER(19), + scope VARCHAR2(2048) +); + +CREATE TABLE system_scope ( + id NUMBER(19) NOT NULL PRIMARY KEY, + scope VARCHAR2(256) NOT NULL, + description VARCHAR2(4000), + icon VARCHAR2(256), + restricted NUMBER(1) DEFAULT 0 NOT NULL, + default_scope NUMBER(1) DEFAULT 0 NOT NULL, + structured NUMBER(1) DEFAULT 0 NOT NULL, + structured_param_description VARCHAR2(256), + + CONSTRAINT system_scope_unique UNIQUE (scope) +); +CREATE SEQUENCE system_scope_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE user_info ( + id NUMBER(19) NOT NULL PRIMARY KEY, + sub VARCHAR2(256), + preferred_username VARCHAR2(256), + name VARCHAR2(256), + given_name VARCHAR2(256), + family_name VARCHAR2(256), + middle_name VARCHAR2(256), + nickname VARCHAR2(256), + profile VARCHAR2(256), + picture VARCHAR2(256), + website VARCHAR2(256), + email VARCHAR2(256), + email_verified NUMBER(1), + gender VARCHAR2(256), + zone_info VARCHAR2(256), + locale VARCHAR2(256), + phone_number VARCHAR2(256), + phone_number_verified NUMBER(1), + address_id VARCHAR2(256), + updated_time VARCHAR2(256), + birthdate VARCHAR2(256), + src VARCHAR2(4000) +); +CREATE SEQUENCE user_info_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE whitelisted_site ( + id NUMBER(19) NOT NULL PRIMARY KEY, + creator_user_id VARCHAR2(256), + client_id VARCHAR2(256) +); +CREATE SEQUENCE whitelisted_site_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE whitelisted_site_scope ( + owner_id NUMBER(19), + scope VARCHAR2(256) +); + +CREATE TABLE pairwise_identifier ( + id NUMBER(19) NOT NULL PRIMARY KEY, + identifier VARCHAR2(256), + sub VARCHAR2(256), + sector_identifier VARCHAR2(2048) +); +CREATE SEQUENCE pairwise_identifier_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE resource_set ( + id NUMBER(19) NOT NULL PRIMARY KEY, + name VARCHAR2(1024) NOT NULL, + uri VARCHAR2(1024), + icon_uri VARCHAR2(1024), + rs_type VARCHAR2(256), + owner VARCHAR2(256) NOT NULL, + client_id VARCHAR2(256) +); +CREATE SEQUENCE resource_set_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE resource_set_scope ( + owner_id NUMBER(19) NOT NULL, + scope VARCHAR2(256) NOT NULL +); + +CREATE TABLE permission_ticket ( + id NUMBER(19) NOT NULL PRIMARY KEY, + ticket VARCHAR2(256) NOT NULL, + permission_id NUMBER(19) NOT NULL, + expiration TIMESTAMP +); +CREATE SEQUENCE permission_ticket_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE permission ( + id NUMBER(19) NOT NULL PRIMARY KEY, + resource_set_id NUMBER(19) +); +CREATE SEQUENCE permission_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE permission_scope ( + owner_id NUMBER(19) NOT NULL, + scope VARCHAR2(256) NOT NULL +); + +CREATE TABLE claim ( + id NUMBER(19) NOT NULL PRIMARY KEY, + name VARCHAR2(256), + friendly_name VARCHAR2(1024), + claim_type VARCHAR2(1024), + claim_value VARCHAR2(1024) +); +CREATE SEQUENCE claim_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE claim_to_policy ( + policy_id NUMBER(19) NOT NULL, + claim_id NUMBER(19) NOT NULL +); + +CREATE TABLE claim_to_permission_ticket ( + permission_ticket_id NUMBER(19) NOT NULL, + claim_id NUMBER(19) NOT NULL +); + +CREATE TABLE policy ( + id NUMBER(19) NOT NULL PRIMARY KEY, + name VARCHAR2(1024), + resource_set_id NUMBER(19) +); +CREATE SEQUENCE policy_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE TABLE policy_scope ( + owner_id NUMBER(19) NOT NULL, + scope VARCHAR2(256) NOT NULL +); + +CREATE TABLE claim_token_format ( + owner_id NUMBER(19) NOT NULL, + claim_token_format VARCHAR2(1024) NOT NULL +); + +CREATE TABLE claim_issuer ( + owner_id NUMBER(19) NOT NULL, + issuer VARCHAR2(1024) NOT NULL +); + +CREATE TABLE saved_registered_client ( + id NUMBER(19) NOT NULL PRIMARY KEY, + issuer VARCHAR2(1024), + registered_client CLOB +); +CREATE SEQUENCE saved_registered_client_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; + +CREATE INDEX at_tv_idx ON access_token(token_value); +CREATE INDEX ts_oi_idx ON token_scope(owner_id); +CREATE INDEX at_exp_idx ON access_token(expiration); +CREATE INDEX rf_ahi_idx ON refresh_token(auth_holder_id); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/security-schema_oracle.sql b/openid-connect-server-webapp/src/main/resources/db/tables/security-schema_oracle.sql new file mode 100644 index 0000000000..b27c6ae6ee --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/tables/security-schema_oracle.sql @@ -0,0 +1,16 @@ +-- +-- Tables for Spring Security's user details service +-- + +create table users( + username varchar2(50) not null primary key, + password varchar2(50) not null, + enabled number(19) not null +); + +create table authorities ( + username varchar2(50) not null, + authority varchar2(50) not null, + constraint fk_authorities_users foreign key(username) references users(username), + constraint ix_authority unique (username,authority) +); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/resources/db/users_oracle.sql b/openid-connect-server-webapp/src/main/resources/db/users_oracle.sql new file mode 100644 index 0000000000..732a13f16e --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/users_oracle.sql @@ -0,0 +1,39 @@ +-- +-- Insert user information into the temporary tables. To add users to the Oracle database, edit things here. +-- + +INSERT INTO users_TEMP (username, password, enabled) VALUES ('admin','password',1); +INSERT INTO users_TEMP (username, password, enabled) VALUES ('user','password',1); + + +INSERT INTO authorities_TEMP (username, authority) VALUES ('admin','ROLE_ADMIN'); +INSERT INTO authorities_TEMP (username, authority) VALUES('admin','ROLE_USER'); +INSERT INTO authorities_TEMP (username, authority) VALUES('user','ROLE_USER'); + +-- By default, the username column here has to match the username column in the users table, above +INSERT INTO user_info_TEMP (sub, preferred_username, name, email, email_verified) VALUES ('90342.ASDFJWFA','admin','Demo Admin','admin@example.com', 1); +INSERT INTO user_info_TEMP (sub, preferred_username, name, email, email_verified) VALUES ('01921.FLANRJQW','user','Demo User','user@example.com', 1); + + +-- +-- Merge the temporary users safely into the database. This is a two-step process to keep users from being created on every startup with a persistent store. +-- + +MERGE INTO users + USING (SELECT username, password, enabled FROM users_TEMP) vals + ON (vals.username = users.username) + WHEN NOT MATCHED THEN + INSERT (username, password, enabled) VALUES(vals.username, vals.password, vals.enabled); + +MERGE INTO authorities + USING (SELECT username, authority FROM authorities_TEMP) vals + ON (vals.username = authorities.username AND vals.authority = authorities.authority) + WHEN NOT MATCHED THEN + INSERT (username,authority) values (vals.username, vals.authority); + +MERGE INTO user_info + USING (SELECT sub, preferred_username, name, email, email_verified FROM user_info_TEMP) vals + ON (vals.preferred_username = user_info.preferred_username) + WHEN NOT MATCHED THEN + INSERT (id, sub, preferred_username, name, email, email_verified) VALUES (user_info_seq.nextval, vals.sub, vals.preferred_username, vals.name, vals.email, + vals.email_verified); diff --git a/openid-connect-server-webapp/src/main/resources/entity-mappings_oracle.xml b/openid-connect-server-webapp/src/main/resources/entity-mappings_oracle.xml new file mode 100644 index 0000000000..b1b074ac8b --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/entity-mappings_oracle.xml @@ -0,0 +1,266 @@ + + + OpenID Connect Server entities + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml index 41b6c86885..13c16ddb7b 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml @@ -94,4 +94,29 @@ --> + + + + + + + + diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml index 5df98b83c4..1b977626ad 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml @@ -48,6 +48,8 @@ + + From 4b3284ffd28d13e1f677658280a35c02c5b3ee47 Mon Sep 17 00:00:00 2001 From: Tomasz Borowiec Date: Fri, 4 Nov 2016 12:34:52 +0100 Subject: [PATCH 071/269] ojdbc6 dependency moved to parent pom, added check constraints for boolean columns, fixed invalid column type --- openid-connect-server-webapp/pom.xml | 7 ----- .../db/tables/loading_temp_tables_oracle.sql | 2 +- .../db/tables/oracle_database_tables.sql | 29 ++++++++++++++----- .../db/tables/security-schema_oracle.sql | 6 ++-- pom.xml | 6 ++++ 5 files changed, 33 insertions(+), 17 deletions(-) diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index 87b645b77d..4f35ad1c57 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -133,13 +133,6 @@ com.zaxxer HikariCP - - - Deployable package of the OpenID Connect server diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/loading_temp_tables_oracle.sql b/openid-connect-server-webapp/src/main/resources/db/tables/loading_temp_tables_oracle.sql index f91f3e633c..c9a1e7f3d6 100644 --- a/openid-connect-server-webapp/src/main/resources/db/tables/loading_temp_tables_oracle.sql +++ b/openid-connect-server-webapp/src/main/resources/db/tables/loading_temp_tables_oracle.sql @@ -74,4 +74,4 @@ CREATE GLOBAL TEMPORARY TABLE system_scope_TEMP ( default_scope NUMBER(1), structured NUMBER(1), structured_param_description VARCHAR2(256) -) ON COMMIT PRESERVE ROWS; \ No newline at end of file +) ON COMMIT PRESERVE ROWS; diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/oracle_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/tables/oracle_database_tables.sql index 106369ed45..5440f32681 100644 --- a/openid-connect-server-webapp/src/main/resources/db/tables/oracle_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/tables/oracle_database_tables.sql @@ -52,7 +52,9 @@ CREATE TABLE authentication_holder ( user_auth_id NUMBER(19), approved NUMBER(1), redirect_uri VARCHAR2(2048), - client_id VARCHAR2(256) + client_id VARCHAR2(256), + + CONSTRAINT approved_check CHECK (approved in (1,0)) ); CREATE SEQUENCE authentication_holder_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; @@ -92,7 +94,9 @@ CREATE TABLE saved_user_auth ( id NUMBER(19) NOT NULL PRIMARY KEY, name VARCHAR2(1024), authenticated NUMBER(1), - source_class VARCHAR2(2048) + source_class VARCHAR2(2048), + + CONSTRAINT authenticated_check CHECK (authenticated in (1,0)) ); CREATE SEQUENCE saved_user_auth_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; @@ -178,9 +182,14 @@ CREATE TABLE client_details ( software_statement VARCHAR2(4000), - code_challenge_method VARCHAR2(256), + code_challenge_method VARCHAR2(256), - CONSTRAINT client_details_unique UNIQUE (client_id) + CONSTRAINT client_details_unique UNIQUE (client_id), + CONSTRAINT reuse_refresh_tokens_check CHECK (reuse_refresh_tokens in (1,0)), + CONSTRAINT dynamically_registered_check CHECK (dynamically_registered in (1,0)), + CONSTRAINT allow_introspection_check CHECK (allow_introspection in (1,0)), + CONSTRAINT require_auth_time_check CHECK (require_auth_time in (1,0)), + CONSTRAINT clear_acc_tok_on_refresh_check CHECK (clear_access_tokens_on_refresh in (1,0)) ); CREATE SEQUENCE client_details_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; @@ -248,7 +257,10 @@ CREATE TABLE system_scope ( structured NUMBER(1) DEFAULT 0 NOT NULL, structured_param_description VARCHAR2(256), - CONSTRAINT system_scope_unique UNIQUE (scope) + CONSTRAINT system_scope_unique UNIQUE (scope), + CONSTRAINT default_scope_check CHECK (default_scope in (1,0)), + CONSTRAINT restricted_check CHECK (restricted in (1,0)), + CONSTRAINT structured_check CHECK (structured in (1,0)) ); CREATE SEQUENCE system_scope_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; @@ -274,7 +286,10 @@ CREATE TABLE user_info ( address_id VARCHAR2(256), updated_time VARCHAR2(256), birthdate VARCHAR2(256), - src VARCHAR2(4000) + src VARCHAR2(4000), + + CONSTRAINT email_verified_check CHECK (email_verified in (1,0)), + CONSTRAINT phone_number_verified_check CHECK (phone_number_verified in (1,0)) ); CREATE SEQUENCE user_info_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; @@ -384,4 +399,4 @@ CREATE SEQUENCE saved_registered_client_seq START WITH 1 INCREMENT BY 1 NOCACHE CREATE INDEX at_tv_idx ON access_token(token_value); CREATE INDEX ts_oi_idx ON token_scope(owner_id); CREATE INDEX at_exp_idx ON access_token(expiration); -CREATE INDEX rf_ahi_idx ON refresh_token(auth_holder_id); \ No newline at end of file +CREATE INDEX rf_ahi_idx ON refresh_token(auth_holder_id); diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/security-schema_oracle.sql b/openid-connect-server-webapp/src/main/resources/db/tables/security-schema_oracle.sql index b27c6ae6ee..5b67ef668f 100644 --- a/openid-connect-server-webapp/src/main/resources/db/tables/security-schema_oracle.sql +++ b/openid-connect-server-webapp/src/main/resources/db/tables/security-schema_oracle.sql @@ -5,7 +5,9 @@ create table users( username varchar2(50) not null primary key, password varchar2(50) not null, - enabled number(19) not null + enabled number(1) not null, + + constraint enabled_check check (enabled in (1, 0)) ); create table authorities ( @@ -13,4 +15,4 @@ create table authorities ( authority varchar2(50) not null, constraint fk_authorities_users foreign key(username) references users(username), constraint ix_authority unique (username,authority) -); \ No newline at end of file +); diff --git a/pom.xml b/pom.xml index b3420ffc98..6b482d1b8d 100644 --- a/pom.xml +++ b/pom.xml @@ -380,6 +380,12 @@ postgresql 9.4-1201-jdbc4 + + org.eclipse.persistence org.eclipse.persistence.jpa From fa122e7ad65abbb1998e663cba4a2eccfd2eca41 Mon Sep 17 00:00:00 2001 From: Tomasz Borowiec Date: Fri, 4 Nov 2016 12:50:18 +0100 Subject: [PATCH 072/269] ojdbc driver uncommented in dep mgmnt --- pom.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 6b482d1b8d..7ff1f71a9a 100644 --- a/pom.xml +++ b/pom.xml @@ -380,12 +380,11 @@ postgresql 9.4-1201-jdbc4 - - + org.eclipse.persistence org.eclipse.persistence.jpa From 8333d035b4aeb66e91670a976297afe2064cced0 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 2 Dec 2016 15:50:15 -0500 Subject: [PATCH 073/269] move database files to match new layouts --- .../main/resources/db/{ => hsql}/clients.sql | 0 .../{tables => hsql}/hsql_database_tables.sql | 0 .../{tables => hsql}/loading_temp_tables.sql | 0 .../main/resources/db/{ => hsql}/scopes.sql | 0 .../db/{tables => hsql}/security-schema.sql | 0 .../main/resources/db/{ => hsql}/users.sql | 0 .../mysql_database_tables.sql | 0 .../resources/db/mysql/security-schema.sql | 14 ++++++++ .../db/{ => oracle}/clients_oracle.sql | 0 .../oracle}/entity-mappings_oracle.xml | 0 .../loading_temp_tables_oracle.sql | 0 .../oracle_database_tables.sql | 0 .../db/{ => oracle}/scopes_oracle.sql | 0 .../security-schema_oracle.sql | 0 .../db/{ => oracle}/users_oracle.sql | 0 .../{tables => psql}/psql_database_tables.sql | 0 .../resources/db/psql/security-schema.sql | 14 ++++++++ .../src/main/webapp/WEB-INF/data-context.xml | 36 +++++++++---------- .../src/main/webapp/WEB-INF/jpa-config.xml | 2 +- 19 files changed, 47 insertions(+), 19 deletions(-) rename openid-connect-server-webapp/src/main/resources/db/{ => hsql}/clients.sql (100%) rename openid-connect-server-webapp/src/main/resources/db/{tables => hsql}/hsql_database_tables.sql (100%) rename openid-connect-server-webapp/src/main/resources/db/{tables => hsql}/loading_temp_tables.sql (100%) rename openid-connect-server-webapp/src/main/resources/db/{ => hsql}/scopes.sql (100%) rename openid-connect-server-webapp/src/main/resources/db/{tables => hsql}/security-schema.sql (100%) rename openid-connect-server-webapp/src/main/resources/db/{ => hsql}/users.sql (100%) rename openid-connect-server-webapp/src/main/resources/db/{tables => mysql}/mysql_database_tables.sql (100%) create mode 100644 openid-connect-server-webapp/src/main/resources/db/mysql/security-schema.sql rename openid-connect-server-webapp/src/main/resources/db/{ => oracle}/clients_oracle.sql (100%) rename openid-connect-server-webapp/src/main/resources/{ => db/oracle}/entity-mappings_oracle.xml (100%) rename openid-connect-server-webapp/src/main/resources/db/{tables => oracle}/loading_temp_tables_oracle.sql (100%) rename openid-connect-server-webapp/src/main/resources/db/{tables => oracle}/oracle_database_tables.sql (100%) rename openid-connect-server-webapp/src/main/resources/db/{ => oracle}/scopes_oracle.sql (100%) rename openid-connect-server-webapp/src/main/resources/db/{tables => oracle}/security-schema_oracle.sql (100%) rename openid-connect-server-webapp/src/main/resources/db/{ => oracle}/users_oracle.sql (100%) rename openid-connect-server-webapp/src/main/resources/db/{tables => psql}/psql_database_tables.sql (100%) create mode 100644 openid-connect-server-webapp/src/main/resources/db/psql/security-schema.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/clients.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/clients.sql similarity index 100% rename from openid-connect-server-webapp/src/main/resources/db/clients.sql rename to openid-connect-server-webapp/src/main/resources/db/hsql/clients.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/hsql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql similarity index 100% rename from openid-connect-server-webapp/src/main/resources/db/tables/hsql_database_tables.sql rename to openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/loading_temp_tables.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/loading_temp_tables.sql similarity index 100% rename from openid-connect-server-webapp/src/main/resources/db/tables/loading_temp_tables.sql rename to openid-connect-server-webapp/src/main/resources/db/hsql/loading_temp_tables.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/scopes.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/scopes.sql similarity index 100% rename from openid-connect-server-webapp/src/main/resources/db/scopes.sql rename to openid-connect-server-webapp/src/main/resources/db/hsql/scopes.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/security-schema.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/security-schema.sql similarity index 100% rename from openid-connect-server-webapp/src/main/resources/db/tables/security-schema.sql rename to openid-connect-server-webapp/src/main/resources/db/hsql/security-schema.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/users.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/users.sql similarity index 100% rename from openid-connect-server-webapp/src/main/resources/db/users.sql rename to openid-connect-server-webapp/src/main/resources/db/hsql/users.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/mysql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql similarity index 100% rename from openid-connect-server-webapp/src/main/resources/db/tables/mysql_database_tables.sql rename to openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/mysql/security-schema.sql b/openid-connect-server-webapp/src/main/resources/db/mysql/security-schema.sql new file mode 100644 index 0000000000..bc5d70b880 --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/mysql/security-schema.sql @@ -0,0 +1,14 @@ +-- +-- Tables for Spring Security's user details service +-- + +create table IF NOT EXISTS users( + username varchar(50) not null primary key, + password varchar(50) not null, + enabled boolean not null); + + create table IF NOT EXISTS authorities ( + username varchar(50) not null, + authority varchar(50) not null, + constraint fk_authorities_users foreign key(username) references users(username), + constraint ix_authority unique (username,authority)); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/resources/db/clients_oracle.sql b/openid-connect-server-webapp/src/main/resources/db/oracle/clients_oracle.sql similarity index 100% rename from openid-connect-server-webapp/src/main/resources/db/clients_oracle.sql rename to openid-connect-server-webapp/src/main/resources/db/oracle/clients_oracle.sql diff --git a/openid-connect-server-webapp/src/main/resources/entity-mappings_oracle.xml b/openid-connect-server-webapp/src/main/resources/db/oracle/entity-mappings_oracle.xml similarity index 100% rename from openid-connect-server-webapp/src/main/resources/entity-mappings_oracle.xml rename to openid-connect-server-webapp/src/main/resources/db/oracle/entity-mappings_oracle.xml diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/loading_temp_tables_oracle.sql b/openid-connect-server-webapp/src/main/resources/db/oracle/loading_temp_tables_oracle.sql similarity index 100% rename from openid-connect-server-webapp/src/main/resources/db/tables/loading_temp_tables_oracle.sql rename to openid-connect-server-webapp/src/main/resources/db/oracle/loading_temp_tables_oracle.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/oracle_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql similarity index 100% rename from openid-connect-server-webapp/src/main/resources/db/tables/oracle_database_tables.sql rename to openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/scopes_oracle.sql b/openid-connect-server-webapp/src/main/resources/db/oracle/scopes_oracle.sql similarity index 100% rename from openid-connect-server-webapp/src/main/resources/db/scopes_oracle.sql rename to openid-connect-server-webapp/src/main/resources/db/oracle/scopes_oracle.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/security-schema_oracle.sql b/openid-connect-server-webapp/src/main/resources/db/oracle/security-schema_oracle.sql similarity index 100% rename from openid-connect-server-webapp/src/main/resources/db/tables/security-schema_oracle.sql rename to openid-connect-server-webapp/src/main/resources/db/oracle/security-schema_oracle.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/users_oracle.sql b/openid-connect-server-webapp/src/main/resources/db/oracle/users_oracle.sql similarity index 100% rename from openid-connect-server-webapp/src/main/resources/db/users_oracle.sql rename to openid-connect-server-webapp/src/main/resources/db/oracle/users_oracle.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/tables/psql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql similarity index 100% rename from openid-connect-server-webapp/src/main/resources/db/tables/psql_database_tables.sql rename to openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/psql/security-schema.sql b/openid-connect-server-webapp/src/main/resources/db/psql/security-schema.sql new file mode 100644 index 0000000000..bc5d70b880 --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/psql/security-schema.sql @@ -0,0 +1,14 @@ +-- +-- Tables for Spring Security's user details service +-- + +create table IF NOT EXISTS users( + username varchar(50) not null primary key, + password varchar(50) not null, + enabled boolean not null); + + create table IF NOT EXISTS authorities ( + username varchar(50) not null, + authority varchar(50) not null, + constraint fk_authorities_users foreign key(username) references users(username), + constraint ix_authority unique (username,authority)); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml index 13c16ddb7b..8143fdc81d 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml @@ -32,14 +32,14 @@ - + - + - - - - + + + + @@ -48,7 +48,7 @@ + src/main/resources/db/mysql/mysql_database_tables.sql --> + src/main/resources/db/psql/psql_database_tables.sql --> + src/main/resources/db/oracle/oracle_database_tables.sql --> - + From 4ac3916db3bd55258c36cf3216d793f78a8491e9 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 2 Dec 2016 15:50:22 -0500 Subject: [PATCH 074/269] spaces to tabs --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 7ff1f71a9a..c5b164a037 100644 --- a/pom.xml +++ b/pom.xml @@ -381,10 +381,10 @@ 9.4-1201-jdbc4 - com.oracle - ojdbc6 - 11.1.0.7.0 - + com.oracle + ojdbc6 + 11.1.0.7.0 + org.eclipse.persistence org.eclipse.persistence.jpa From b2fab9642ee7502a218ae1330ca90e391a1cda6c Mon Sep 17 00:00:00 2001 From: Sofia Ang Date: Tue, 25 Oct 2016 08:15:11 +0800 Subject: [PATCH 075/269] Fix such that `user_id` is only added if user authentication is available OAuth2Authentication#getPrincipal() used by OAuth2Authentication#getName() defaults to the client id if user authentication is not available. Prior to this fix, an introspection of a client-only access token would result to the user_id also being the client_id. This causes problems when this introspection result is converted into an OAuth2Authentication by a resource server's IntrospectingTokenService -- the user_id is populated with the client_id and so OAuth2Authentication's userAuthentication is populated falsely. --- .../service/impl/DefaultIntrospectionResultAssembler.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java index 7d69ca19df..e630667d2f 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java @@ -92,7 +92,9 @@ public Map assembleFrom(OAuth2AccessTokenEntity accessToken, Use result.put(SUB, authentication.getName()); } - result.put(USER_ID, authentication.getName()); + if(authentication.getUserAuthentication() != null) { + result.put(USER_ID, authentication.getUserAuthentication().getName()); + } result.put(CLIENT_ID, authentication.getOAuth2Request().getClientId()); @@ -131,7 +133,9 @@ public Map assembleFrom(OAuth2RefreshTokenEntity refreshToken, U result.put(SUB, authentication.getName()); } - result.put(USER_ID, authentication.getName()); + if(authentication.getUserAuthentication() != null) { + result.put(USER_ID, authentication.getUserAuthentication().getName()); + } result.put(CLIENT_ID, authentication.getOAuth2Request().getClientId()); From d361f01999beb3b2fa99769ec816ff55bec6ac8e Mon Sep 17 00:00:00 2001 From: Sofia Ang Date: Tue, 25 Oct 2016 08:21:10 +0800 Subject: [PATCH 076/269] Fix such that the OAuth2Authentication returned would have a `null` userAuthentication if `user_id` is not found during introspection `sub` cannot be used to create the user authentication because it may not necessarily refer to the user. Instead if may refer to the client if the access token happens to be client-only. --- .../IntrospectingTokenService.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java index d3e61e52d1..606102d07d 100644 --- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java +++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java @@ -235,8 +235,13 @@ private OAuth2Request createStoredRequest(final JsonObject token) { return storedRequest; } - private Authentication createAuthentication(JsonObject token) { - return new PreAuthenticatedAuthenticationToken(token.get("sub").getAsString(), token, introspectionAuthorityGranter.getAuthorities(token)); + private Authentication createUserAuthentication(JsonObject token) { + JsonElement userId = token.get("user_id"); + if(userId == null) { + return null; + } + + return new PreAuthenticatedAuthenticationToken(userId.getAsString(), token, introspectionAuthorityGranter.getAuthorities(token)); } private OAuth2AccessToken createAccessToken(final JsonObject token, final String tokenString) { @@ -321,7 +326,7 @@ protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOE return null; } // create an OAuth2Authentication - OAuth2Authentication auth = new OAuth2Authentication(createStoredRequest(tokenResponse), createAuthentication(tokenResponse)); + OAuth2Authentication auth = new OAuth2Authentication(createStoredRequest(tokenResponse), createUserAuthentication(tokenResponse)); // create an OAuth2AccessToken OAuth2AccessToken token = createAccessToken(tokenResponse, accessToken); From 52da5e769ad5e46c3c8cbedbba644296df58d72c Mon Sep 17 00:00:00 2001 From: Sofia Ang Date: Tue, 25 Oct 2016 16:00:28 +0800 Subject: [PATCH 077/269] Fix test by returning a new OAuth2Authentication instead of mocking it --- ...stDefaultIntrospectionResultAssembler.java | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java index 7562436e0c..c4e1e1ce95 100644 --- a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java +++ b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java @@ -30,6 +30,8 @@ import org.mitre.oauth2.service.IntrospectionResultAssembler; import org.mitre.openid.connect.model.UserInfo; import org.mitre.uma.model.Permission; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; @@ -59,7 +61,7 @@ public void shouldAssembleExpectedResultForAccessToken() throws ParseException { // given OAuth2AccessTokenEntity accessToken = accessToken(new Date(123 * 1000L), scopes("foo", "bar"), null, "Bearer", - authentication("name", request("clientId"))); + oauth2AuthenticationWithUser(oauth2Request("clientId"), "name")); UserInfo userInfo = userInfo("sub"); @@ -89,7 +91,7 @@ public void shouldAssembleExpectedResultForAccessToken_withPermissions() throws // given OAuth2AccessTokenEntity accessToken = accessToken(new Date(123 * 1000L), scopes("foo", "bar"), permissions(permission(1L, "foo", "bar")), - "Bearer", authentication("name", request("clientId"))); + "Bearer", oauth2AuthenticationWithUser(oauth2Request("clientId"), "name")); UserInfo userInfo = userInfo("sub"); @@ -127,7 +129,7 @@ public void shouldAssembleExpectedResultForAccessTokenWithoutUserInfo() throws P // given OAuth2AccessTokenEntity accessToken = accessToken(new Date(123 * 1000L), scopes("foo", "bar"), null, "Bearer", - authentication("name", request("clientId"))); + oauth2AuthenticationWithUser(oauth2Request("clientId"), "name")); Set authScopes = scopes("foo", "bar", "baz"); @@ -154,7 +156,7 @@ public void shouldAssembleExpectedResultForAccessTokenWithoutExpiry() { // given OAuth2AccessTokenEntity accessToken = accessToken(null, scopes("foo", "bar"), null, "Bearer", - authentication("name", request("clientId"))); + oauth2AuthenticationWithUser(oauth2Request("clientId"), "name")); UserInfo userInfo = userInfo("sub"); @@ -181,7 +183,7 @@ public void shouldAssembleExpectedResultForRefreshToken() throws ParseException // given OAuth2RefreshTokenEntity refreshToken = refreshToken(new Date(123 * 1000L), - authentication("name", request("clientId", scopes("foo", "bar")))); + oauth2AuthenticationWithUser(oauth2Request("clientId", scopes("foo", "bar")), "name")); UserInfo userInfo = userInfo("sub"); @@ -209,7 +211,7 @@ public void shouldAssembleExpectedResultForRefreshTokenWithoutUserInfo() throws // given OAuth2RefreshTokenEntity refreshToken = refreshToken(new Date(123 * 1000L), - authentication("name", request("clientId", scopes("foo", "bar")))); + oauth2AuthenticationWithUser(oauth2Request("clientId", scopes("foo", "bar")), "name")); Set authScopes = scopes("foo", "bar", "baz"); @@ -235,7 +237,7 @@ public void shouldAssembleExpectedResultForRefreshTokenWithoutExpiry() { // given OAuth2RefreshTokenEntity refreshToken = refreshToken(null, - authentication("name", request("clientId", scopes("foo", "bar")))); + oauth2AuthenticationWithUser(oauth2Request("clientId", scopes("foo", "bar")), "name")); UserInfo userInfo = userInfo("sub"); @@ -279,18 +281,20 @@ private OAuth2RefreshTokenEntity refreshToken(Date exp, OAuth2Authentication aut return refreshToken; } - private OAuth2Authentication authentication(String name, OAuth2Request request) { - OAuth2Authentication authentication = mock(OAuth2Authentication.class); - given(authentication.getName()).willReturn(name); - given(authentication.getOAuth2Request()).willReturn(request); - return authentication; + private OAuth2Authentication oauth2AuthenticationWithUser(OAuth2Request request, String username) { + UsernamePasswordAuthenticationToken userAuthentication = new UsernamePasswordAuthenticationToken(username, "somepassword"); + return oauth2Authentication(request, userAuthentication); } - private OAuth2Request request(String clientId) { - return request(clientId, null); + private OAuth2Authentication oauth2Authentication(OAuth2Request request, Authentication userAuthentication) { + return new OAuth2Authentication(request, userAuthentication); + } + + private OAuth2Request oauth2Request(String clientId) { + return oauth2Request(clientId, null); } - private OAuth2Request request(String clientId, Set scopes) { + private OAuth2Request oauth2Request(String clientId, Set scopes) { return new OAuth2Request(null, clientId, null, true, scopes, null, null, null, null); } From bb6bb81dbcfc8c1d835c437bedaa70e51f2e966f Mon Sep 17 00:00:00 2001 From: Sofia Ang Date: Tue, 25 Oct 2016 16:12:11 +0800 Subject: [PATCH 078/269] Add new tests which asserts that `user_id` should not be present in the introspection response if there's no user authentication available --- ...stDefaultIntrospectionResultAssembler.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java index c4e1e1ce95..aa754592af 100644 --- a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java +++ b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java @@ -178,6 +178,31 @@ public void shouldAssembleExpectedResultForAccessTokenWithoutExpiry() { assertThat(result, is(equalTo(expected))); } + @Test + public void shouldAssembleExpectedResultForAccessTokenWithoutUserAuthentication() throws ParseException { + // given + OAuth2AccessTokenEntity accessToken = accessToken(new Date(123 * 1000L), scopes("foo", "bar"), null, "Bearer", + oauth2Authentication(oauth2Request("clientId"), null)); + + Set authScopes = scopes("foo", "bar", "baz"); + + // when + Map result = assembler.assembleFrom(accessToken, null, authScopes); + + + // then `user_id` should not be present + Map expected = new ImmutableMap.Builder() + .put("sub", "clientId") + .put("exp", 123L) + .put("expires_at", dateFormat.valueToString(new Date(123 * 1000L))) + .put("scope", "bar foo") + .put("active", Boolean.TRUE) + .put("client_id", "clientId") + .put("token_type", "Bearer") + .build(); + assertThat(result, is(equalTo(expected))); + } + @Test public void shouldAssembleExpectedResultForRefreshToken() throws ParseException { @@ -258,6 +283,30 @@ public void shouldAssembleExpectedResultForRefreshTokenWithoutExpiry() { assertThat(result, is(equalTo(expected))); } + @Test + public void shouldAssembleExpectedResultForRefreshTokenWithoutUserAuthentication() throws ParseException { + // given + OAuth2RefreshTokenEntity refreshToken = refreshToken(null, + oauth2Authentication(oauth2Request("clientId", scopes("foo", "bar")), null)); + + Set authScopes = scopes("foo", "bar", "baz"); + + // when + Map result = assembler.assembleFrom(refreshToken, null, authScopes); + + + // then `user_id` should not be present + Map expected = new ImmutableMap.Builder() + .put("sub", "clientId") + .put("scope", "bar foo") + .put("active", Boolean.TRUE) + .put("client_id", "clientId") + .build(); + assertThat(result, is(equalTo(expected))); + } + + + private UserInfo userInfo(String sub) { UserInfo userInfo = mock(UserInfo.class); given(userInfo.getSub()).willReturn(sub); From 476ec872ff0f2caddb684c80b5802af5ccab7883 Mon Sep 17 00:00:00 2001 From: Oleksandr Shpota Date: Sat, 24 Sep 2016 11:45:34 +0300 Subject: [PATCH 079/269] Fixed NPE in case if algorithm is not specified --- .../openid/connect/client/OIDCAuthenticationFilter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java index c35304aaf6..c8b28e86cf 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java @@ -381,9 +381,9 @@ protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOE JWSAlgorithm alg = clientConfig.getTokenEndpointAuthSigningAlg(); if (SECRET_JWT.equals(clientConfig.getTokenEndpointAuthMethod()) && - (alg.equals(JWSAlgorithm.HS256) - || alg.equals(JWSAlgorithm.HS384) - || alg.equals(JWSAlgorithm.HS512))) { + (JWSAlgorithm.HS256.equals(alg) + || JWSAlgorithm.HS384.equals(alg) + || JWSAlgorithm.HS512.equals(alg))) { // generate one based on client secret signer = symmetricCacheService.getSymmetricValidtor(clientConfig.getClient()); From f45a6ef56a6a51e28272e24959ab26da7caeeef2 Mon Sep 17 00:00:00 2001 From: ngriesser Date: Wed, 21 Sep 2016 15:57:40 +0200 Subject: [PATCH 080/269] use the same encoding as on client side see OIDCAuthenticationFilter line 336 --- .../oauth2/service/impl/UriEncodedClientUserDetailsService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java index b2ab65642a..bbb1848162 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java @@ -68,7 +68,7 @@ public UserDetails loadUserByUsername(String clientId) throws UsernameNotFoundE if (client != null) { - String encodedPassword = UriUtils.encodeQueryParam(Strings.nullToEmpty(client.getClientSecret()), "UTF-8"); + String encodedPassword = UriUtils.encodePathSegment(Strings.nullToEmpty(client.getClientSecret()), "UTF-8"); if (config.isHeartMode() || // if we're running HEART mode turn off all client secrets (client.getTokenEndpointAuthMethod() != null && From c3d0c18af5c5d3c4fe226d2ef998d71e64d873c1 Mon Sep 17 00:00:00 2001 From: Julian Schlichtholz Date: Fri, 16 Sep 2016 16:10:11 +0200 Subject: [PATCH 081/269] make HttpClient configurable, closes #1071 --- .../IntrospectingTokenService.java | 13 +++++++--- .../client/OIDCAuthenticationFilter.java | 18 +++++++------ .../connect/client/UserInfoFetcher.java | 17 ++++++++----- ...egistrationClientConfigurationService.java | 20 ++++++++++----- .../DynamicServerConfigurationService.java | 15 +++++++---- .../service/impl/WebfingerIssuerService.java | 15 +++++++---- .../service/impl/JWKSetCacheService.java | 25 +++++++++++++------ ...faultOAuth2ClientDetailsEntityService.java | 12 ++++++--- .../InMemoryClientLogoLoadingService.java | 22 +++++++++++----- 9 files changed, 105 insertions(+), 52 deletions(-) diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java index 606102d07d..5f86419a17 100644 --- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java +++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java @@ -73,10 +73,15 @@ public class IntrospectingTokenService implements ResourceServerTokenServices { private boolean cacheNonExpiringTokens = false; private boolean cacheTokens = true; - private HttpClient httpClient = HttpClientBuilder.create() - .useSystemProperties() - .build(); - private HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); + private HttpComponentsClientHttpRequestFactory factory; + + public IntrospectingTokenService() { + this(HttpClientBuilder.create().useSystemProperties().build()); + } + + public IntrospectingTokenService(HttpClient httpClient) { + this.factory = new HttpComponentsClientHttpRequestFactory(httpClient); + } // Inner class to store in the hash map private class TokenCacheObject { diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java index c8b28e86cf..5059f70369 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java @@ -119,6 +119,8 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi @Autowired(required=false) private JWTSigningAndValidationService authenticationSignerService; + @Autowired(required=false) + private HttpClient httpClient; /* * Modular services to build out client filter. @@ -341,14 +343,14 @@ protected Authentication handleAuthorizationCodeResponse(HttpServletRequest requ // Handle Token Endpoint interaction - HttpClient httpClient = HttpClientBuilder.create() - .useSystemProperties() - .setDefaultRequestConfig( - RequestConfig.custom() - .setSocketTimeout(httpSocketTimeout) - .build() - ) - .build(); + if(httpClient == null) { + httpClient = HttpClientBuilder.create() + .useSystemProperties() + .setDefaultRequestConfig(RequestConfig.custom() + .setSocketTimeout(httpSocketTimeout) + .build()) + .build(); + } HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java index cbaaf4fd06..f1142d970c 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java @@ -61,10 +61,14 @@ public class UserInfoFetcher { private LoadingCache cache; public UserInfoFetcher() { + this(HttpClientBuilder.create().useSystemProperties().build()); + } + + public UserInfoFetcher(HttpClient httpClient) { cache = CacheBuilder.newBuilder() .expireAfterWrite(1, TimeUnit.HOURS) // expires 1 hour after fetch .maximumSize(100) - .build(new UserInfoLoader()); + .build(new UserInfoLoader(httpClient)); } public UserInfo loadUserInfo(final PendingOIDCAuthenticationToken token) { @@ -79,11 +83,12 @@ public UserInfo loadUserInfo(final PendingOIDCAuthenticationToken token) { private class UserInfoLoader extends CacheLoader { - private HttpClient httpClient = HttpClientBuilder.create() - .useSystemProperties() - .build(); - private HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); - + private HttpComponentsClientHttpRequestFactory factory; + + UserInfoLoader(HttpClient httpClient) { + this.factory = new HttpComponentsClientHttpRequestFactory(httpClient); + } + public UserInfo load(final PendingOIDCAuthenticationToken token) { ServerConfiguration serverConfiguration = token.getServerConfiguration(); diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicRegistrationClientConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicRegistrationClientConfigurationService.java index 8a820923c0..266d55479a 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicRegistrationClientConfigurationService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicRegistrationClientConfigurationService.java @@ -72,7 +72,11 @@ public class DynamicRegistrationClientConfigurationService implements ClientConf private Set blacklist = new HashSet<>(); public DynamicRegistrationClientConfigurationService() { - clients = CacheBuilder.newBuilder().build(new DynamicClientRegistrationLoader()); + this(HttpClientBuilder.create().useSystemProperties().build()); + } + + public DynamicRegistrationClientConfigurationService(HttpClient httpClient) { + clients = CacheBuilder.newBuilder().build(new DynamicClientRegistrationLoader(httpClient)); } @Override @@ -168,13 +172,17 @@ public void setBlacklist(Set blacklist) { * */ public class DynamicClientRegistrationLoader extends CacheLoader { - private HttpClient httpClient = HttpClientBuilder.create() - .useSystemProperties() - .build(); - - private HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + private HttpComponentsClientHttpRequestFactory httpFactory; private Gson gson = new Gson(); // note that this doesn't serialize nulls by default + public DynamicClientRegistrationLoader() { + this(HttpClientBuilder.create().useSystemProperties().build()); + } + + public DynamicClientRegistrationLoader(HttpClient httpClient) { + this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + } + @Override public RegisteredClient load(ServerConfiguration serverConfig) throws Exception { RestTemplate restTemplate = new RestTemplate(httpFactory); diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java index 1ddc6aa7bb..d26713d3cd 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java @@ -69,8 +69,12 @@ public class DynamicServerConfigurationService implements ServerConfigurationSer private Set blacklist = new HashSet<>(); public DynamicServerConfigurationService() { + this(HttpClientBuilder.create().useSystemProperties().build()); + } + + public DynamicServerConfigurationService(HttpClient httpClient) { // initialize the cache - servers = CacheBuilder.newBuilder().build(new OpenIDConnectServiceConfigurationFetcher()); + servers = CacheBuilder.newBuilder().build(new OpenIDConnectServiceConfigurationFetcher(httpClient)); } /** @@ -126,12 +130,13 @@ public ServerConfiguration getServerConfiguration(String issuer) { * */ private class OpenIDConnectServiceConfigurationFetcher extends CacheLoader { - private HttpClient httpClient = HttpClientBuilder.create() - .useSystemProperties() - .build(); - private HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + private HttpComponentsClientHttpRequestFactory httpFactory; private JsonParser parser = new JsonParser(); + OpenIDConnectServiceConfigurationFetcher(HttpClient httpClient) { + this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + } + @Override public ServerConfiguration load(String issuer) throws Exception { RestTemplate restTemplate = new RestTemplate(httpFactory); diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java index 0eedb0ffb7..87a03ef9d0 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java @@ -94,7 +94,11 @@ public LoadingResult(String loginHint, String issuer) { private boolean forceHttps = true; public WebfingerIssuerService() { - issuers = CacheBuilder.newBuilder().build(new WebfingerIssuerFetcher()); + this(HttpClientBuilder.create().useSystemProperties().build()); + } + + public WebfingerIssuerService(HttpClient httpClient) { + issuers = CacheBuilder.newBuilder().build(new WebfingerIssuerFetcher(httpClient)); } /* (non-Javadoc) @@ -203,12 +207,13 @@ public void setForceHttps(boolean forceHttps) { * */ private class WebfingerIssuerFetcher extends CacheLoader { - private HttpClient httpClient = HttpClientBuilder.create() - .useSystemProperties() - .build(); - private HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + private HttpComponentsClientHttpRequestFactory httpFactory; private JsonParser parser = new JsonParser(); + WebfingerIssuerFetcher(HttpClient httpClient) { + this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + } + @Override public LoadingResult load(String identifier) throws Exception { diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JWKSetCacheService.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JWKSetCacheService.java index e078905296..766615186c 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JWKSetCacheService.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JWKSetCacheService.java @@ -68,11 +68,11 @@ public JWKSetCacheService() { this.validators = CacheBuilder.newBuilder() .expireAfterWrite(1, TimeUnit.HOURS) // expires 1 hour after fetch .maximumSize(100) - .build(new JWKSetVerifierFetcher()); + .build(new JWKSetVerifierFetcher(HttpClientBuilder.create().useSystemProperties().build())); this.encrypters = CacheBuilder.newBuilder() .expireAfterWrite(1, TimeUnit.HOURS) // expires 1 hour after fetch .maximumSize(100) - .build(new JWKSetEncryptorFetcher()); + .build(new JWKSetEncryptorFetcher(HttpClientBuilder.create().useSystemProperties().build())); } /** @@ -104,9 +104,13 @@ public JWTEncryptionAndDecryptionService getEncrypter(String jwksUri) { * */ private class JWKSetVerifierFetcher extends CacheLoader { - private HttpClient httpClient = HttpClientBuilder.create().useSystemProperties().build(); - private HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient); - private RestTemplate restTemplate = new RestTemplate(httpFactory); + private HttpComponentsClientHttpRequestFactory httpFactory; + private RestTemplate restTemplate; + + JWKSetVerifierFetcher(HttpClient httpClient) { + this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + this.restTemplate = new RestTemplate(httpFactory); + } /** * Load the JWK Set and build the appropriate signing service. @@ -130,9 +134,14 @@ public JWTSigningAndValidationService load(String key) throws Exception { * */ private class JWKSetEncryptorFetcher extends CacheLoader { - private HttpClient httpClient = HttpClientBuilder.create().useSystemProperties().build(); - private HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient); - private RestTemplate restTemplate = new RestTemplate(httpFactory); + private HttpComponentsClientHttpRequestFactory httpFactory; + private RestTemplate restTemplate; + + public JWKSetEncryptorFetcher(HttpClient httpClient) { + this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + this.restTemplate = new RestTemplate(httpFactory); + } + /* (non-Javadoc) * @see com.google.common.cache.CacheLoader#load(java.lang.Object) */ diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ClientDetailsEntityService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ClientDetailsEntityService.java index d1da007fd5..19e46b5819 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ClientDetailsEntityService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ClientDetailsEntityService.java @@ -103,7 +103,7 @@ public class DefaultOAuth2ClientDetailsEntityService implements ClientDetailsEnt private LoadingCache> sectorRedirects = CacheBuilder.newBuilder() .expireAfterAccess(1, TimeUnit.HOURS) .maximumSize(100) - .build(new SectorIdentifierLoader()); + .build(new SectorIdentifierLoader(HttpClientBuilder.create().useSystemProperties().build())); @Override public ClientDetailsEntity saveNewClient(ClientDetailsEntity client) { @@ -465,11 +465,15 @@ public ClientDetailsEntity generateClientSecret(ClientDetailsEntity client) { * */ private class SectorIdentifierLoader extends CacheLoader> { - private HttpClient httpClient = HttpClientBuilder.create().useSystemProperties().build(); - private HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient); - private RestTemplate restTemplate = new RestTemplate(httpFactory); + private HttpComponentsClientHttpRequestFactory httpFactory; + private RestTemplate restTemplate; private JsonParser parser = new JsonParser(); + SectorIdentifierLoader(HttpClient httpClient) { + this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + this.restTemplate = new RestTemplate(httpFactory); + } + @Override public List load(String key) throws Exception { diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/InMemoryClientLogoLoadingService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/InMemoryClientLogoLoadingService.java index 314b1a8313..5039cd79eb 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/InMemoryClientLogoLoadingService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/InMemoryClientLogoLoadingService.java @@ -48,17 +48,20 @@ public class InMemoryClientLogoLoadingService implements ClientLogoLoadingService { private LoadingCache cache; - - + + public InMemoryClientLogoLoadingService() { + this(HttpClientBuilder.create().useSystemProperties().build()); + } + /** * */ - public InMemoryClientLogoLoadingService() { + public InMemoryClientLogoLoadingService(HttpClient httpClient) { cache = CacheBuilder.newBuilder() .maximumSize(100) .expireAfterAccess(14, TimeUnit.DAYS) - .build(new ClientLogoFetcher()); + .build(new ClientLogoFetcher(httpClient)); } @@ -84,8 +87,15 @@ public CachedImage getLogo(ClientDetailsEntity client) { * */ public class ClientLogoFetcher extends CacheLoader { - private HttpClient httpClient = HttpClientBuilder.create().useSystemProperties().build(); - private HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + private HttpClient httpClient; + + public ClientLogoFetcher() { + this(HttpClientBuilder.create().useSystemProperties().build()); + } + + public ClientLogoFetcher(HttpClient httpClient) { + this.httpClient = httpClient; + } /* (non-Javadoc) * @see com.google.common.cache.CacheLoader#load(java.lang.Object) From 7725fcfa2b2ea3625b7df84e6bd8e5cb22375201 Mon Sep 17 00:00:00 2001 From: HeXetic Date: Fri, 15 Jul 2016 11:06:45 -0400 Subject: [PATCH 082/269] createAuthorizationCode should be @Transactional An Authentication should not exist without its matching AuthorizationCode, but typically an AuthorizationCode will have a foreign key on an Authentication, meaning it can't be saved first. This block should be wrapped in a transaction so that other DB clients (say, for example, clearExpiredAuthorizationCodes) don't see an inconsistent snapshot and then misbehave. --- .../service/impl/DefaultOAuth2AuthorizationCodeService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java index b4089c8a45..d6445d1bc1 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java @@ -66,6 +66,7 @@ public class DefaultOAuth2AuthorizationCodeService implements AuthorizationCodeS * @return the authorization code */ @Override + @Transactional(value="defaultTransactionManager") public String createAuthorizationCode(OAuth2Authentication authentication) { String code = generator.generate(); From d875d52be7e6c2ce8de8c45b326a5bd75f1c4eb2 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 8 Dec 2016 17:01:55 -0500 Subject: [PATCH 083/269] updated data import/export services for 1.3 --- .../connect/service/MITREidDataService.java | 1 + .../service/impl/MITREidDataService_1_2.java | 362 +--- .../service/impl/MITREidDataService_1_3.java | 1292 +++++++++++ .../org/mitre/openid/connect/web/DataAPI.java | 8 +- .../impl/TestMITREidDataService_1_2.java | 885 -------- .../impl/TestMITREidDataService_1_3.java | 1887 +++++++++++++++++ 6 files changed, 3188 insertions(+), 1247 deletions(-) create mode 100644 openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java create mode 100644 openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataService.java index be82505f5b..64f81091fe 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataService.java @@ -33,6 +33,7 @@ public interface MITREidDataService { public static final String MITREID_CONNECT_1_0 = "mitreid-connect-1.0"; public static final String MITREID_CONNECT_1_1 = "mitreid-connect-1.1"; public static final String MITREID_CONNECT_1_2 = "mitreid-connect-1.2"; + public static final String MITREID_CONNECT_1_3 = "mitreid-connect-1.3"; // member names public static final String REFRESHTOKENS = "refreshTokens"; diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java index 4440e3d374..bc8658ed85 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java @@ -170,367 +170,7 @@ public class MITREidDataService_1_2 extends MITREidDataServiceSupport implements @Override public void exportData(JsonWriter writer) throws IOException { - // version tag at the root - writer.name(MITREID_CONNECT_1_2); - - writer.beginObject(); - - // clients list - writer.name(CLIENTS); - writer.beginArray(); - writeClients(writer); - writer.endArray(); - - writer.name(GRANTS); - writer.beginArray(); - writeGrants(writer); - writer.endArray(); - - writer.name(WHITELISTEDSITES); - writer.beginArray(); - writeWhitelistedSites(writer); - writer.endArray(); - - writer.name(BLACKLISTEDSITES); - writer.beginArray(); - writeBlacklistedSites(writer); - writer.endArray(); - - writer.name(AUTHENTICATIONHOLDERS); - writer.beginArray(); - writeAuthenticationHolders(writer); - writer.endArray(); - - writer.name(ACCESSTOKENS); - writer.beginArray(); - writeAccessTokens(writer); - writer.endArray(); - - writer.name(REFRESHTOKENS); - writer.beginArray(); - writeRefreshTokens(writer); - writer.endArray(); - - writer.name(SYSTEMSCOPES); - writer.beginArray(); - writeSystemScopes(writer); - writer.endArray(); - - writer.endObject(); // end mitreid-connect-1.2 - } - - /** - * @param writer - */ - private void writeRefreshTokens(JsonWriter writer) throws IOException { - for (OAuth2RefreshTokenEntity token : tokenRepository.getAllRefreshTokens()) { - writer.beginObject(); - writer.name(ID).value(token.getId()); - writer.name(EXPIRATION).value(toUTCString(token.getExpiration())); - writer.name(CLIENT_ID) - .value((token.getClient() != null) ? token.getClient().getClientId() : null); - writer.name(AUTHENTICATION_HOLDER_ID) - .value((token.getAuthenticationHolder() != null) ? token.getAuthenticationHolder().getId() : null); - writer.name(VALUE).value(token.getValue()); - writer.endObject(); - logger.debug("Wrote refresh token {}", token.getId()); - } - logger.info("Done writing refresh tokens"); - } - - /** - * @param writer - */ - private void writeAccessTokens(JsonWriter writer) throws IOException { - for (OAuth2AccessTokenEntity token : tokenRepository.getAllAccessTokens()) { - writer.beginObject(); - writer.name(ID).value(token.getId()); - writer.name(EXPIRATION).value(toUTCString(token.getExpiration())); - writer.name(CLIENT_ID) - .value((token.getClient() != null) ? token.getClient().getClientId() : null); - writer.name(AUTHENTICATION_HOLDER_ID) - .value((token.getAuthenticationHolder() != null) ? token.getAuthenticationHolder().getId() : null); - writer.name(REFRESH_TOKEN_ID) - .value((token.getRefreshToken() != null) ? token.getRefreshToken().getId() : null); - writer.name(ID_TOKEN_ID) - .value((token.getIdToken() != null) ? token.getIdToken().getId() : null); - writer.name(SCOPE); - writer.beginArray(); - for (String s : token.getScope()) { - writer.value(s); - } - writer.endArray(); - writer.name(TYPE).value(token.getTokenType()); - writer.name(VALUE).value(token.getValue()); - writer.endObject(); - logger.debug("Wrote access token {}", token.getId()); - } - logger.info("Done writing access tokens"); - } - - /** - * @param writer - */ - private void writeAuthenticationHolders(JsonWriter writer) throws IOException { - for (AuthenticationHolderEntity holder : authHolderRepository.getAll()) { - writer.beginObject(); - writer.name(ID).value(holder.getId()); - - writer.name(REQUEST_PARAMETERS); - writer.beginObject(); - for (Entry entry : holder.getRequestParameters().entrySet()) { - writer.name(entry.getKey()).value(entry.getValue()); - } - writer.endObject(); - writer.name(CLIENT_ID).value(holder.getClientId()); - Set scope = holder.getScope(); - writer.name(SCOPE); - writer.beginArray(); - for (String s : scope) { - writer.value(s); - } - writer.endArray(); - writer.name(RESOURCE_IDS); - writer.beginArray(); - if (holder.getResourceIds() != null) { - for (String s : holder.getResourceIds()) { - writer.value(s); - } - } - writer.endArray(); - writer.name(AUTHORITIES); - writer.beginArray(); - for (GrantedAuthority authority : holder.getAuthorities()) { - writer.value(authority.getAuthority()); - } - writer.endArray(); - writer.name(APPROVED).value(holder.isApproved()); - writer.name(REDIRECT_URI).value(holder.getRedirectUri()); - writer.name(RESPONSE_TYPES); - writer.beginArray(); - for (String s : holder.getResponseTypes()) { - writer.value(s); - } - writer.endArray(); - writer.name(EXTENSIONS); - writer.beginObject(); - for (Entry entry : holder.getExtensions().entrySet()) { - // while the extension map itself is Serializable, we enforce storage of Strings - if (entry.getValue() instanceof String) { - writer.name(entry.getKey()).value((String) entry.getValue()); - } else { - logger.warn("Skipping non-string extension: " + entry); - } - } - writer.endObject(); - - writer.name(SAVED_USER_AUTHENTICATION); - if (holder.getUserAuth() != null) { - writer.beginObject(); - writer.name(NAME).value(holder.getUserAuth().getName()); - writer.name(SOURCE_CLASS).value(holder.getUserAuth().getSourceClass()); - writer.name(AUTHENTICATED).value(holder.getUserAuth().isAuthenticated()); - writer.name(AUTHORITIES); - writer.beginArray(); - for (GrantedAuthority authority : holder.getUserAuth().getAuthorities()) { - writer.value(authority.getAuthority()); - } - writer.endArray(); - - writer.endObject(); - } else { - writer.nullValue(); - } - - - writer.endObject(); - logger.debug("Wrote authentication holder {}", holder.getId()); - } - logger.info("Done writing authentication holders"); - } - - /** - * @param writer - */ - private void writeGrants(JsonWriter writer) throws IOException { - for (ApprovedSite site : approvedSiteRepository.getAll()) { - writer.beginObject(); - writer.name(ID).value(site.getId()); - writer.name(ACCESS_DATE).value(toUTCString(site.getAccessDate())); - writer.name(CLIENT_ID).value(site.getClientId()); - writer.name(CREATION_DATE).value(toUTCString(site.getCreationDate())); - writer.name(TIMEOUT_DATE).value(toUTCString(site.getTimeoutDate())); - writer.name(USER_ID).value(site.getUserId()); - writer.name(ALLOWED_SCOPES); - writeNullSafeArray(writer, site.getAllowedScopes()); - Set tokens = site.getApprovedAccessTokens(); - writer.name(APPROVED_ACCESS_TOKENS); - writer.beginArray(); - for (OAuth2AccessTokenEntity token : tokens) { - writer.value(token.getId()); - } - writer.endArray(); - writer.endObject(); - logger.debug("Wrote grant {}", site.getId()); - } - logger.info("Done writing grants"); - } - - /** - * @param writer - */ - private void writeWhitelistedSites(JsonWriter writer) throws IOException { - for (WhitelistedSite wlSite : wlSiteRepository.getAll()) { - writer.beginObject(); - writer.name(ID).value(wlSite.getId()); - writer.name(CLIENT_ID).value(wlSite.getClientId()); - writer.name(CREATOR_USER_ID).value(wlSite.getCreatorUserId()); - writer.name(ALLOWED_SCOPES); - writeNullSafeArray(writer, wlSite.getAllowedScopes()); - writer.endObject(); - logger.debug("Wrote whitelisted site {}", wlSite.getId()); - } - logger.info("Done writing whitelisted sites"); - } - - /** - * @param writer - */ - private void writeBlacklistedSites(JsonWriter writer) throws IOException { - for (BlacklistedSite blSite : blSiteRepository.getAll()) { - writer.beginObject(); - writer.name(ID).value(blSite.getId()); - writer.name(URI).value(blSite.getUri()); - writer.endObject(); - logger.debug("Wrote blacklisted site {}", blSite.getId()); - } - logger.info("Done writing blacklisted sites"); - } - - /** - * @param writer - */ - private void writeClients(JsonWriter writer) { - for (ClientDetailsEntity client : clientRepository.getAllClients()) { - try { - writer.beginObject(); - writer.name(CLIENT_ID).value(client.getClientId()); - writer.name(RESOURCE_IDS); - writeNullSafeArray(writer, client.getResourceIds()); - - writer.name(SECRET).value(client.getClientSecret()); - - writer.name(SCOPE); - writeNullSafeArray(writer, client.getScope()); - - writer.name(AUTHORITIES); - writer.beginArray(); - for (GrantedAuthority authority : client.getAuthorities()) { - writer.value(authority.getAuthority()); - } - writer.endArray(); - writer.name(ACCESS_TOKEN_VALIDITY_SECONDS).value(client.getAccessTokenValiditySeconds()); - writer.name(REFRESH_TOKEN_VALIDITY_SECONDS).value(client.getRefreshTokenValiditySeconds()); - writer.name(REDIRECT_URIS); - writeNullSafeArray(writer, client.getRedirectUris()); - writer.name(CLAIMS_REDIRECT_URIS); - writeNullSafeArray(writer, client.getClaimsRedirectUris()); - writer.name(NAME).value(client.getClientName()); - writer.name(URI).value(client.getClientUri()); - writer.name(LOGO_URI).value(client.getLogoUri()); - writer.name(CONTACTS); - writeNullSafeArray(writer, client.getContacts()); - writer.name(TOS_URI).value(client.getTosUri()); - writer.name(TOKEN_ENDPOINT_AUTH_METHOD) - .value((client.getTokenEndpointAuthMethod() != null) ? client.getTokenEndpointAuthMethod().getValue() : null); - writer.name(GRANT_TYPES); - writer.beginArray(); - for (String s : client.getGrantTypes()) { - writer.value(s); - } - writer.endArray(); - writer.name(RESPONSE_TYPES); - writer.beginArray(); - for (String s : client.getResponseTypes()) { - writer.value(s); - } - writer.endArray(); - writer.name(POLICY_URI).value(client.getPolicyUri()); - writer.name(JWKS_URI).value(client.getJwksUri()); - writer.name(JWKS).value((client.getJwks() != null) ? client.getJwks().toString() : null); - writer.name(APPLICATION_TYPE) - .value((client.getApplicationType() != null) ? client.getApplicationType().getValue() : null); - writer.name(SECTOR_IDENTIFIER_URI).value(client.getSectorIdentifierUri()); - writer.name(SUBJECT_TYPE) - .value((client.getSubjectType() != null) ? client.getSubjectType().getValue() : null); - writer.name(REQUEST_OBJECT_SIGNING_ALG) - .value((client.getRequestObjectSigningAlg() != null) ? client.getRequestObjectSigningAlg().getName() : null); - writer.name(ID_TOKEN_SIGNED_RESPONSE_ALG) - .value((client.getIdTokenSignedResponseAlg() != null) ? client.getIdTokenSignedResponseAlg().getName() : null); - writer.name(ID_TOKEN_ENCRYPTED_RESPONSE_ALG) - .value((client.getIdTokenEncryptedResponseAlg() != null) ? client.getIdTokenEncryptedResponseAlg().getName() : null); - writer.name(ID_TOKEN_ENCRYPTED_RESPONSE_ENC) - .value((client.getIdTokenEncryptedResponseEnc() != null) ? client.getIdTokenEncryptedResponseEnc().getName() : null); - writer.name(USER_INFO_SIGNED_RESPONSE_ALG) - .value((client.getUserInfoSignedResponseAlg() != null) ? client.getUserInfoSignedResponseAlg().getName() : null); - writer.name(USER_INFO_ENCRYPTED_RESPONSE_ALG) - .value((client.getUserInfoEncryptedResponseAlg() != null) ? client.getUserInfoEncryptedResponseAlg().getName() : null); - writer.name(USER_INFO_ENCRYPTED_RESPONSE_ENC) - .value((client.getUserInfoEncryptedResponseEnc() != null) ? client.getUserInfoEncryptedResponseEnc().getName() : null); - writer.name(TOKEN_ENDPOINT_AUTH_SIGNING_ALG) - .value((client.getTokenEndpointAuthSigningAlg() != null) ? client.getTokenEndpointAuthSigningAlg().getName() : null); - writer.name(DEFAULT_MAX_AGE).value(client.getDefaultMaxAge()); - Boolean requireAuthTime = null; - try { - requireAuthTime = client.getRequireAuthTime(); - } catch (NullPointerException e) { - } - if (requireAuthTime != null) { - writer.name(REQUIRE_AUTH_TIME).value(requireAuthTime); - } - writer.name(DEFAULT_ACR_VALUES); - writeNullSafeArray(writer, client.getDefaultACRvalues()); - writer.name(INTITATE_LOGIN_URI).value(client.getInitiateLoginUri()); - writer.name(POST_LOGOUT_REDIRECT_URI); - writeNullSafeArray(writer, client.getPostLogoutRedirectUris()); - writer.name(REQUEST_URIS); - writeNullSafeArray(writer, client.getRequestUris()); - writer.name(DESCRIPTION).value(client.getClientDescription()); - writer.name(ALLOW_INTROSPECTION).value(client.isAllowIntrospection()); - writer.name(REUSE_REFRESH_TOKEN).value(client.isReuseRefreshToken()); - writer.name(CLEAR_ACCESS_TOKENS_ON_REFRESH).value(client.isClearAccessTokensOnRefresh()); - writer.name(DYNAMICALLY_REGISTERED).value(client.isDynamicallyRegistered()); - writer.endObject(); - logger.debug("Wrote client {}", client.getId()); - } catch (IOException ex) { - logger.error("Unable to write client {}", client.getId(), ex); - } - } - logger.info("Done writing clients"); - } - - /** - * @param writer - */ - private void writeSystemScopes(JsonWriter writer) { - for (SystemScope sysScope : sysScopeRepository.getAll()) { - try { - writer.beginObject(); - writer.name(ID).value(sysScope.getId()); - writer.name(DESCRIPTION).value(sysScope.getDescription()); - writer.name(ICON).value(sysScope.getIcon()); - writer.name(VALUE).value(sysScope.getValue()); - writer.name(RESTRICTED).value(sysScope.isRestricted()); - writer.name(STRUCTURED).value(sysScope.isStructured()); - writer.name(STRUCTURED_PARAMETER).value(sysScope.getStructuredParamDescription()); - writer.name(DEFAULT_SCOPE).value(sysScope.isDefaultScope()); - writer.endObject(); - logger.debug("Wrote system scope {}", sysScope.getId()); - } catch (IOException ex) { - logger.error("Unable to write system scope {}", sysScope.getId(), ex); - } - } - logger.info("Done writing system scopes"); + throw new UnsupportedOperationException("Can not export 1.2 format from this version."); } /* (non-Javadoc) diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java new file mode 100644 index 0000000000..1bb9476d1d --- /dev/null +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java @@ -0,0 +1,1292 @@ +/******************************************************************************* + * Copyright 2016 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.openid.connect.service.impl; + +import java.io.IOException; +import java.io.Serializable; +import java.text.ParseException; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.mitre.oauth2.model.AuthenticationHolderEntity; +import org.mitre.oauth2.model.ClientDetailsEntity; +import org.mitre.oauth2.model.ClientDetailsEntity.AppType; +import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; +import org.mitre.oauth2.model.ClientDetailsEntity.SubjectType; +import org.mitre.oauth2.model.OAuth2AccessTokenEntity; +import org.mitre.oauth2.model.OAuth2RefreshTokenEntity; +import org.mitre.oauth2.model.PKCEAlgorithm; +import org.mitre.oauth2.model.SavedUserAuthentication; +import org.mitre.oauth2.model.SystemScope; +import org.mitre.oauth2.repository.AuthenticationHolderRepository; +import org.mitre.oauth2.repository.OAuth2ClientRepository; +import org.mitre.oauth2.repository.OAuth2TokenRepository; +import org.mitre.oauth2.repository.SystemScopeRepository; +import org.mitre.openid.connect.model.ApprovedSite; +import org.mitre.openid.connect.model.BlacklistedSite; +import org.mitre.openid.connect.model.WhitelistedSite; +import org.mitre.openid.connect.repository.ApprovedSiteRepository; +import org.mitre.openid.connect.repository.BlacklistedSiteRepository; +import org.mitre.openid.connect.repository.WhitelistedSiteRepository; +import org.mitre.openid.connect.service.MITREidDataService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.stereotype.Service; + +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.nimbusds.jose.EncryptionMethod; +import com.nimbusds.jose.JWEAlgorithm; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jwt.JWTParser; + +import static org.mitre.util.JsonUtils.readMap; +import static org.mitre.util.JsonUtils.readSet; +import static org.mitre.util.JsonUtils.writeNullSafeArray; + +/** + * + * Data service to import and export MITREid 1.2 configuration. + * + * @author jricher + * @author arielak + */ +@Service +@SuppressWarnings(value = {"unchecked"}) +public class MITREidDataService_1_3 extends MITREidDataServiceSupport implements MITREidDataService { + + private static final String DEFAULT_SCOPE = "defaultScope"; + private static final String STRUCTURED_PARAMETER = "structuredParameter"; + private static final String STRUCTURED = "structured"; + private static final String RESTRICTED = "restricted"; + private static final String ICON = "icon"; + private static final String DYNAMICALLY_REGISTERED = "dynamicallyRegistered"; + private static final String CLEAR_ACCESS_TOKENS_ON_REFRESH = "clearAccessTokensOnRefresh"; + private static final String REUSE_REFRESH_TOKEN = "reuseRefreshToken"; + private static final String ALLOW_INTROSPECTION = "allowIntrospection"; + private static final String DESCRIPTION = "description"; + private static final String REQUEST_URIS = "requestUris"; + private static final String POST_LOGOUT_REDIRECT_URI = "postLogoutRedirectUri"; + private static final String INTITATE_LOGIN_URI = "intitateLoginUri"; + private static final String DEFAULT_ACR_VALUES = "defaultACRValues"; + private static final String REQUIRE_AUTH_TIME = "requireAuthTime"; + private static final String DEFAULT_MAX_AGE = "defaultMaxAge"; + private static final String TOKEN_ENDPOINT_AUTH_SIGNING_ALG = "tokenEndpointAuthSigningAlg"; + private static final String USER_INFO_ENCRYPTED_RESPONSE_ENC = "userInfoEncryptedResponseEnc"; + private static final String USER_INFO_ENCRYPTED_RESPONSE_ALG = "userInfoEncryptedResponseAlg"; + private static final String USER_INFO_SIGNED_RESPONSE_ALG = "userInfoSignedResponseAlg"; + private static final String ID_TOKEN_ENCRYPTED_RESPONSE_ENC = "idTokenEncryptedResponseEnc"; + private static final String ID_TOKEN_ENCRYPTED_RESPONSE_ALG = "idTokenEncryptedResponseAlg"; + private static final String ID_TOKEN_SIGNED_RESPONSE_ALG = "idTokenSignedResponseAlg"; + private static final String REQUEST_OBJECT_SIGNING_ALG = "requestObjectSigningAlg"; + private static final String SUBJECT_TYPE = "subjectType"; + private static final String SECTOR_IDENTIFIER_URI = "sectorIdentifierUri"; + private static final String APPLICATION_TYPE = "applicationType"; + private static final String JWKS = "jwks"; + private static final String JWKS_URI = "jwksUri"; + private static final String POLICY_URI = "policyUri"; + private static final String GRANT_TYPES = "grantTypes"; + private static final String TOKEN_ENDPOINT_AUTH_METHOD = "tokenEndpointAuthMethod"; + private static final String TOS_URI = "tosUri"; + private static final String CONTACTS = "contacts"; + private static final String LOGO_URI = "logoUri"; + private static final String REDIRECT_URIS = "redirectUris"; + private static final String REFRESH_TOKEN_VALIDITY_SECONDS = "refreshTokenValiditySeconds"; + private static final String ACCESS_TOKEN_VALIDITY_SECONDS = "accessTokenValiditySeconds"; + private static final String SECRET = "secret"; + private static final String URI = "uri"; + private static final String CREATOR_USER_ID = "creatorUserId"; + private static final String APPROVED_ACCESS_TOKENS = "approvedAccessTokens"; + private static final String ALLOWED_SCOPES = "allowedScopes"; + private static final String USER_ID = "userId"; + private static final String TIMEOUT_DATE = "timeoutDate"; + private static final String CREATION_DATE = "creationDate"; + private static final String ACCESS_DATE = "accessDate"; + private static final String AUTHENTICATED = "authenticated"; + private static final String SOURCE_CLASS = "sourceClass"; + private static final String NAME = "name"; + private static final String SAVED_USER_AUTHENTICATION = "savedUserAuthentication"; + private static final String EXTENSIONS = "extensions"; + private static final String RESPONSE_TYPES = "responseTypes"; + private static final String REDIRECT_URI = "redirectUri"; + private static final String APPROVED = "approved"; + private static final String AUTHORITIES = "authorities"; + private static final String RESOURCE_IDS = "resourceIds"; + private static final String REQUEST_PARAMETERS = "requestParameters"; + private static final String TYPE = "type"; + private static final String SCOPE = "scope"; + private static final String ID_TOKEN_ID = "idTokenId"; + private static final String REFRESH_TOKEN_ID = "refreshTokenId"; + private static final String VALUE = "value"; + private static final String AUTHENTICATION_HOLDER_ID = "authenticationHolderId"; + private static final String CLIENT_ID = "clientId"; + private static final String EXPIRATION = "expiration"; + private static final String CLAIMS_REDIRECT_URIS = "claimsRedirectUris"; + private static final String ID = "id"; + private static final String CODE_CHALLENGE_METHOD = "codeChallengeMethod"; + private static final String SOFTWARE_STATEMENT = "softwareStatement"; + + /** + * Logger for this class + */ + private static final Logger logger = LoggerFactory.getLogger(MITREidDataService_1_3.class); + @Autowired + private OAuth2ClientRepository clientRepository; + @Autowired + private ApprovedSiteRepository approvedSiteRepository; + @Autowired + private WhitelistedSiteRepository wlSiteRepository; + @Autowired + private BlacklistedSiteRepository blSiteRepository; + @Autowired + private AuthenticationHolderRepository authHolderRepository; + @Autowired + private OAuth2TokenRepository tokenRepository; + @Autowired + private SystemScopeRepository sysScopeRepository; + + /* (non-Javadoc) + * @see org.mitre.openid.connect.service.MITREidDataService#export(com.google.gson.stream.JsonWriter) + */ + @Override + public void exportData(JsonWriter writer) throws IOException { + + // version tag at the root + writer.name(MITREID_CONNECT_1_3); + + writer.beginObject(); + + // clients list + writer.name(CLIENTS); + writer.beginArray(); + writeClients(writer); + writer.endArray(); + + writer.name(GRANTS); + writer.beginArray(); + writeGrants(writer); + writer.endArray(); + + writer.name(WHITELISTEDSITES); + writer.beginArray(); + writeWhitelistedSites(writer); + writer.endArray(); + + writer.name(BLACKLISTEDSITES); + writer.beginArray(); + writeBlacklistedSites(writer); + writer.endArray(); + + writer.name(AUTHENTICATIONHOLDERS); + writer.beginArray(); + writeAuthenticationHolders(writer); + writer.endArray(); + + writer.name(ACCESSTOKENS); + writer.beginArray(); + writeAccessTokens(writer); + writer.endArray(); + + writer.name(REFRESHTOKENS); + writer.beginArray(); + writeRefreshTokens(writer); + writer.endArray(); + + writer.name(SYSTEMSCOPES); + writer.beginArray(); + writeSystemScopes(writer); + writer.endArray(); + + writer.endObject(); // end mitreid-connect-1.2 + } + + /** + * @param writer + */ + private void writeRefreshTokens(JsonWriter writer) throws IOException { + for (OAuth2RefreshTokenEntity token : tokenRepository.getAllRefreshTokens()) { + writer.beginObject(); + writer.name(ID).value(token.getId()); + writer.name(EXPIRATION).value(toUTCString(token.getExpiration())); + writer.name(CLIENT_ID) + .value((token.getClient() != null) ? token.getClient().getClientId() : null); + writer.name(AUTHENTICATION_HOLDER_ID) + .value((token.getAuthenticationHolder() != null) ? token.getAuthenticationHolder().getId() : null); + writer.name(VALUE).value(token.getValue()); + writer.endObject(); + logger.debug("Wrote refresh token {}", token.getId()); + } + logger.info("Done writing refresh tokens"); + } + + /** + * @param writer + */ + private void writeAccessTokens(JsonWriter writer) throws IOException { + for (OAuth2AccessTokenEntity token : tokenRepository.getAllAccessTokens()) { + writer.beginObject(); + writer.name(ID).value(token.getId()); + writer.name(EXPIRATION).value(toUTCString(token.getExpiration())); + writer.name(CLIENT_ID) + .value((token.getClient() != null) ? token.getClient().getClientId() : null); + writer.name(AUTHENTICATION_HOLDER_ID) + .value((token.getAuthenticationHolder() != null) ? token.getAuthenticationHolder().getId() : null); + writer.name(REFRESH_TOKEN_ID) + .value((token.getRefreshToken() != null) ? token.getRefreshToken().getId() : null); + writer.name(ID_TOKEN_ID) + .value((token.getIdToken() != null) ? token.getIdToken().getId() : null); + writer.name(SCOPE); + writer.beginArray(); + for (String s : token.getScope()) { + writer.value(s); + } + writer.endArray(); + writer.name(TYPE).value(token.getTokenType()); + writer.name(VALUE).value(token.getValue()); + writer.endObject(); + logger.debug("Wrote access token {}", token.getId()); + } + logger.info("Done writing access tokens"); + } + + /** + * @param writer + */ + private void writeAuthenticationHolders(JsonWriter writer) throws IOException { + for (AuthenticationHolderEntity holder : authHolderRepository.getAll()) { + writer.beginObject(); + writer.name(ID).value(holder.getId()); + + writer.name(REQUEST_PARAMETERS); + writer.beginObject(); + for (Entry entry : holder.getRequestParameters().entrySet()) { + writer.name(entry.getKey()).value(entry.getValue()); + } + writer.endObject(); + writer.name(CLIENT_ID).value(holder.getClientId()); + Set scope = holder.getScope(); + writer.name(SCOPE); + writer.beginArray(); + for (String s : scope) { + writer.value(s); + } + writer.endArray(); + writer.name(RESOURCE_IDS); + writer.beginArray(); + if (holder.getResourceIds() != null) { + for (String s : holder.getResourceIds()) { + writer.value(s); + } + } + writer.endArray(); + writer.name(AUTHORITIES); + writer.beginArray(); + for (GrantedAuthority authority : holder.getAuthorities()) { + writer.value(authority.getAuthority()); + } + writer.endArray(); + writer.name(APPROVED).value(holder.isApproved()); + writer.name(REDIRECT_URI).value(holder.getRedirectUri()); + writer.name(RESPONSE_TYPES); + writer.beginArray(); + for (String s : holder.getResponseTypes()) { + writer.value(s); + } + writer.endArray(); + writer.name(EXTENSIONS); + writer.beginObject(); + for (Entry entry : holder.getExtensions().entrySet()) { + // while the extension map itself is Serializable, we enforce storage of Strings + if (entry.getValue() instanceof String) { + writer.name(entry.getKey()).value((String) entry.getValue()); + } else { + logger.warn("Skipping non-string extension: " + entry); + } + } + writer.endObject(); + + writer.name(SAVED_USER_AUTHENTICATION); + if (holder.getUserAuth() != null) { + writer.beginObject(); + writer.name(NAME).value(holder.getUserAuth().getName()); + writer.name(SOURCE_CLASS).value(holder.getUserAuth().getSourceClass()); + writer.name(AUTHENTICATED).value(holder.getUserAuth().isAuthenticated()); + writer.name(AUTHORITIES); + writer.beginArray(); + for (GrantedAuthority authority : holder.getUserAuth().getAuthorities()) { + writer.value(authority.getAuthority()); + } + writer.endArray(); + + writer.endObject(); + } else { + writer.nullValue(); + } + + + writer.endObject(); + logger.debug("Wrote authentication holder {}", holder.getId()); + } + logger.info("Done writing authentication holders"); + } + + /** + * @param writer + */ + private void writeGrants(JsonWriter writer) throws IOException { + for (ApprovedSite site : approvedSiteRepository.getAll()) { + writer.beginObject(); + writer.name(ID).value(site.getId()); + writer.name(ACCESS_DATE).value(toUTCString(site.getAccessDate())); + writer.name(CLIENT_ID).value(site.getClientId()); + writer.name(CREATION_DATE).value(toUTCString(site.getCreationDate())); + writer.name(TIMEOUT_DATE).value(toUTCString(site.getTimeoutDate())); + writer.name(USER_ID).value(site.getUserId()); + writer.name(ALLOWED_SCOPES); + writeNullSafeArray(writer, site.getAllowedScopes()); + Set tokens = site.getApprovedAccessTokens(); + writer.name(APPROVED_ACCESS_TOKENS); + writer.beginArray(); + for (OAuth2AccessTokenEntity token : tokens) { + writer.value(token.getId()); + } + writer.endArray(); + writer.endObject(); + logger.debug("Wrote grant {}", site.getId()); + } + logger.info("Done writing grants"); + } + + /** + * @param writer + */ + private void writeWhitelistedSites(JsonWriter writer) throws IOException { + for (WhitelistedSite wlSite : wlSiteRepository.getAll()) { + writer.beginObject(); + writer.name(ID).value(wlSite.getId()); + writer.name(CLIENT_ID).value(wlSite.getClientId()); + writer.name(CREATOR_USER_ID).value(wlSite.getCreatorUserId()); + writer.name(ALLOWED_SCOPES); + writeNullSafeArray(writer, wlSite.getAllowedScopes()); + writer.endObject(); + logger.debug("Wrote whitelisted site {}", wlSite.getId()); + } + logger.info("Done writing whitelisted sites"); + } + + /** + * @param writer + */ + private void writeBlacklistedSites(JsonWriter writer) throws IOException { + for (BlacklistedSite blSite : blSiteRepository.getAll()) { + writer.beginObject(); + writer.name(ID).value(blSite.getId()); + writer.name(URI).value(blSite.getUri()); + writer.endObject(); + logger.debug("Wrote blacklisted site {}", blSite.getId()); + } + logger.info("Done writing blacklisted sites"); + } + + /** + * @param writer + */ + private void writeClients(JsonWriter writer) { + for (ClientDetailsEntity client : clientRepository.getAllClients()) { + try { + writer.beginObject(); + writer.name(CLIENT_ID).value(client.getClientId()); + writer.name(RESOURCE_IDS); + writeNullSafeArray(writer, client.getResourceIds()); + + writer.name(SECRET).value(client.getClientSecret()); + + writer.name(SCOPE); + writeNullSafeArray(writer, client.getScope()); + + writer.name(AUTHORITIES); + writer.beginArray(); + for (GrantedAuthority authority : client.getAuthorities()) { + writer.value(authority.getAuthority()); + } + writer.endArray(); + writer.name(ACCESS_TOKEN_VALIDITY_SECONDS).value(client.getAccessTokenValiditySeconds()); + writer.name(REFRESH_TOKEN_VALIDITY_SECONDS).value(client.getRefreshTokenValiditySeconds()); + writer.name(REDIRECT_URIS); + writeNullSafeArray(writer, client.getRedirectUris()); + writer.name(CLAIMS_REDIRECT_URIS); + writeNullSafeArray(writer, client.getClaimsRedirectUris()); + writer.name(NAME).value(client.getClientName()); + writer.name(URI).value(client.getClientUri()); + writer.name(LOGO_URI).value(client.getLogoUri()); + writer.name(CONTACTS); + writeNullSafeArray(writer, client.getContacts()); + writer.name(TOS_URI).value(client.getTosUri()); + writer.name(TOKEN_ENDPOINT_AUTH_METHOD) + .value((client.getTokenEndpointAuthMethod() != null) ? client.getTokenEndpointAuthMethod().getValue() : null); + writer.name(GRANT_TYPES); + writer.beginArray(); + for (String s : client.getGrantTypes()) { + writer.value(s); + } + writer.endArray(); + writer.name(RESPONSE_TYPES); + writer.beginArray(); + for (String s : client.getResponseTypes()) { + writer.value(s); + } + writer.endArray(); + writer.name(POLICY_URI).value(client.getPolicyUri()); + writer.name(JWKS_URI).value(client.getJwksUri()); + writer.name(JWKS).value((client.getJwks() != null) ? client.getJwks().toString() : null); + writer.name(APPLICATION_TYPE) + .value((client.getApplicationType() != null) ? client.getApplicationType().getValue() : null); + writer.name(SECTOR_IDENTIFIER_URI).value(client.getSectorIdentifierUri()); + writer.name(SUBJECT_TYPE) + .value((client.getSubjectType() != null) ? client.getSubjectType().getValue() : null); + writer.name(REQUEST_OBJECT_SIGNING_ALG) + .value((client.getRequestObjectSigningAlg() != null) ? client.getRequestObjectSigningAlg().getName() : null); + writer.name(ID_TOKEN_SIGNED_RESPONSE_ALG) + .value((client.getIdTokenSignedResponseAlg() != null) ? client.getIdTokenSignedResponseAlg().getName() : null); + writer.name(ID_TOKEN_ENCRYPTED_RESPONSE_ALG) + .value((client.getIdTokenEncryptedResponseAlg() != null) ? client.getIdTokenEncryptedResponseAlg().getName() : null); + writer.name(ID_TOKEN_ENCRYPTED_RESPONSE_ENC) + .value((client.getIdTokenEncryptedResponseEnc() != null) ? client.getIdTokenEncryptedResponseEnc().getName() : null); + writer.name(USER_INFO_SIGNED_RESPONSE_ALG) + .value((client.getUserInfoSignedResponseAlg() != null) ? client.getUserInfoSignedResponseAlg().getName() : null); + writer.name(USER_INFO_ENCRYPTED_RESPONSE_ALG) + .value((client.getUserInfoEncryptedResponseAlg() != null) ? client.getUserInfoEncryptedResponseAlg().getName() : null); + writer.name(USER_INFO_ENCRYPTED_RESPONSE_ENC) + .value((client.getUserInfoEncryptedResponseEnc() != null) ? client.getUserInfoEncryptedResponseEnc().getName() : null); + writer.name(TOKEN_ENDPOINT_AUTH_SIGNING_ALG) + .value((client.getTokenEndpointAuthSigningAlg() != null) ? client.getTokenEndpointAuthSigningAlg().getName() : null); + writer.name(DEFAULT_MAX_AGE).value(client.getDefaultMaxAge()); + Boolean requireAuthTime = null; + try { + requireAuthTime = client.getRequireAuthTime(); + } catch (NullPointerException e) { + } + if (requireAuthTime != null) { + writer.name(REQUIRE_AUTH_TIME).value(requireAuthTime); + } + writer.name(DEFAULT_ACR_VALUES); + writeNullSafeArray(writer, client.getDefaultACRvalues()); + writer.name(INTITATE_LOGIN_URI).value(client.getInitiateLoginUri()); + writer.name(POST_LOGOUT_REDIRECT_URI); + writeNullSafeArray(writer, client.getPostLogoutRedirectUris()); + writer.name(REQUEST_URIS); + writeNullSafeArray(writer, client.getRequestUris()); + writer.name(DESCRIPTION).value(client.getClientDescription()); + writer.name(ALLOW_INTROSPECTION).value(client.isAllowIntrospection()); + writer.name(REUSE_REFRESH_TOKEN).value(client.isReuseRefreshToken()); + writer.name(CLEAR_ACCESS_TOKENS_ON_REFRESH).value(client.isClearAccessTokensOnRefresh()); + writer.name(DYNAMICALLY_REGISTERED).value(client.isDynamicallyRegistered()); + writer.name(CODE_CHALLENGE_METHOD).value(client.getCodeChallengeMethod() != null ? client.getCodeChallengeMethod().getName() : null); + writer.name(SOFTWARE_STATEMENT).value(client.getSoftwareStatement() != null ? client.getSoftwareStatement().serialize() : null); + writer.endObject(); + logger.debug("Wrote client {}", client.getId()); + } catch (IOException ex) { + logger.error("Unable to write client {}", client.getId(), ex); + } + } + logger.info("Done writing clients"); + } + + /** + * @param writer + */ + private void writeSystemScopes(JsonWriter writer) { + for (SystemScope sysScope : sysScopeRepository.getAll()) { + try { + writer.beginObject(); + writer.name(ID).value(sysScope.getId()); + writer.name(DESCRIPTION).value(sysScope.getDescription()); + writer.name(ICON).value(sysScope.getIcon()); + writer.name(VALUE).value(sysScope.getValue()); + writer.name(RESTRICTED).value(sysScope.isRestricted()); + writer.name(STRUCTURED).value(sysScope.isStructured()); + writer.name(STRUCTURED_PARAMETER).value(sysScope.getStructuredParamDescription()); + writer.name(DEFAULT_SCOPE).value(sysScope.isDefaultScope()); + writer.endObject(); + logger.debug("Wrote system scope {}", sysScope.getId()); + } catch (IOException ex) { + logger.error("Unable to write system scope {}", sysScope.getId(), ex); + } + } + logger.info("Done writing system scopes"); + } + + /* (non-Javadoc) + * @see org.mitre.openid.connect.service.MITREidDataService#importData(com.google.gson.stream.JsonReader) + */ + @Override + public void importData(JsonReader reader) throws IOException { + + logger.info("Reading configuration for 1.2"); + + // this *HAS* to start as an object + reader.beginObject(); + + while (reader.hasNext()) { + JsonToken tok = reader.peek(); + switch (tok) { + case NAME: + String name = reader.nextName(); + // find out which member it is + if (name.equals(CLIENTS)) { + readClients(reader); + } else if (name.equals(GRANTS)) { + readGrants(reader); + } else if (name.equals(WHITELISTEDSITES)) { + readWhitelistedSites(reader); + } else if (name.equals(BLACKLISTEDSITES)) { + readBlacklistedSites(reader); + } else if (name.equals(AUTHENTICATIONHOLDERS)) { + readAuthenticationHolders(reader); + } else if (name.equals(ACCESSTOKENS)) { + readAccessTokens(reader); + } else if (name.equals(REFRESHTOKENS)) { + readRefreshTokens(reader); + } else if (name.equals(SYSTEMSCOPES)) { + readSystemScopes(reader); + } else { + // unknown token, skip it + reader.skipValue(); + } + break; + case END_OBJECT: + // the object ended, we're done here + reader.endObject(); + continue; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + fixObjectReferences(); + } + private Map refreshTokenToClientRefs = new HashMap(); + private Map refreshTokenToAuthHolderRefs = new HashMap(); + private Map refreshTokenOldToNewIdMap = new HashMap(); + + /** + * @param reader + * @throws IOException + */ + /** + * @param reader + * @throws IOException + */ + private void readRefreshTokens(JsonReader reader) throws IOException { + reader.beginArray(); + while (reader.hasNext()) { + OAuth2RefreshTokenEntity token = new OAuth2RefreshTokenEntity(); + reader.beginObject(); + Long currentId = null; + String clientId = null; + Long authHolderId = null; + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ID)) { + currentId = reader.nextLong(); + } else if (name.equals(EXPIRATION)) { + Date date = utcToDate(reader.nextString()); + token.setExpiration(date); + } else if (name.equals(VALUE)) { + String value = reader.nextString(); + try { + token.setJwt(JWTParser.parse(value)); + } catch (ParseException ex) { + logger.error("Unable to set refresh token value to {}", value, ex); + } + } else if (name.equals(CLIENT_ID)) { + clientId = reader.nextString(); + } else if (name.equals(AUTHENTICATION_HOLDER_ID)) { + authHolderId = reader.nextLong(); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + Long newId = tokenRepository.saveRefreshToken(token).getId(); + refreshTokenToClientRefs.put(currentId, clientId); + refreshTokenToAuthHolderRefs.put(currentId, authHolderId); + refreshTokenOldToNewIdMap.put(currentId, newId); + logger.debug("Read refresh token {}", currentId); + } + reader.endArray(); + logger.info("Done reading refresh tokens"); + } + private Map accessTokenToClientRefs = new HashMap(); + private Map accessTokenToAuthHolderRefs = new HashMap(); + private Map accessTokenToRefreshTokenRefs = new HashMap(); + private Map accessTokenToIdTokenRefs = new HashMap(); + private Map accessTokenOldToNewIdMap = new HashMap(); + + /** + * @param reader + * @throws IOException + */ + /** + * @param reader + * @throws IOException + */ + private void readAccessTokens(JsonReader reader) throws IOException { + reader.beginArray(); + while (reader.hasNext()) { + OAuth2AccessTokenEntity token = new OAuth2AccessTokenEntity(); + reader.beginObject(); + Long currentId = null; + String clientId = null; + Long authHolderId = null; + Long refreshTokenId = null; + Long idTokenId = null; + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ID)) { + currentId = reader.nextLong(); + } else if (name.equals(EXPIRATION)) { + Date date = utcToDate(reader.nextString()); + token.setExpiration(date); + } else if (name.equals(VALUE)) { + String value = reader.nextString(); + try { + // all tokens are JWTs + token.setJwt(JWTParser.parse(value)); + } catch (ParseException ex) { + logger.error("Unable to set refresh token value to {}", value, ex); + } + } else if (name.equals(CLIENT_ID)) { + clientId = reader.nextString(); + } else if (name.equals(AUTHENTICATION_HOLDER_ID)) { + authHolderId = reader.nextLong(); + } else if (name.equals(REFRESH_TOKEN_ID)) { + refreshTokenId = reader.nextLong(); + } else if (name.equals(ID_TOKEN_ID)) { + idTokenId = reader.nextLong(); + } else if (name.equals(SCOPE)) { + Set scope = readSet(reader); + token.setScope(scope); + } else if (name.equals(TYPE)) { + token.setTokenType(reader.nextString()); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + Long newId = tokenRepository.saveAccessToken(token).getId(); + accessTokenToClientRefs.put(currentId, clientId); + accessTokenToAuthHolderRefs.put(currentId, authHolderId); + if (refreshTokenId != null) { + accessTokenToRefreshTokenRefs.put(currentId, refreshTokenId); + } + if (idTokenId != null) { + accessTokenToIdTokenRefs.put(currentId, idTokenId); + } + accessTokenOldToNewIdMap.put(currentId, newId); + logger.debug("Read access token {}", currentId); + } + reader.endArray(); + logger.info("Done reading access tokens"); + } + private Map authHolderOldToNewIdMap = new HashMap(); + + /** + * @param reader + * @throws IOException + */ + private void readAuthenticationHolders(JsonReader reader) throws IOException { + reader.beginArray(); + while (reader.hasNext()) { + AuthenticationHolderEntity ahe = new AuthenticationHolderEntity(); + reader.beginObject(); + Long currentId = null; + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ID)) { + currentId = reader.nextLong(); + } else if (name.equals(REQUEST_PARAMETERS)) { + ahe.setRequestParameters(readMap(reader)); + } else if (name.equals(CLIENT_ID)) { + ahe.setClientId(reader.nextString()); + } else if (name.equals(SCOPE)) { + ahe.setScope(readSet(reader)); + } else if (name.equals(RESOURCE_IDS)) { + ahe.setResourceIds(readSet(reader)); + } else if (name.equals(AUTHORITIES)) { + Set authorityStrs = readSet(reader); + Set authorities = new HashSet(); + for (String s : authorityStrs) { + GrantedAuthority ga = new SimpleGrantedAuthority(s); + authorities.add(ga); + } + ahe.setAuthorities(authorities); + } else if (name.equals(APPROVED)) { + ahe.setApproved(reader.nextBoolean()); + } else if (name.equals(REDIRECT_URI)) { + ahe.setRedirectUri(reader.nextString()); + } else if (name.equals(RESPONSE_TYPES)) { + ahe.setResponseTypes(readSet(reader)); + } else if (name.equals(EXTENSIONS)) { + ahe.setExtensions(readMap(reader)); + } else if (name.equals(SAVED_USER_AUTHENTICATION)) { + ahe.setUserAuth(readSavedUserAuthentication(reader)); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + Long newId = authHolderRepository.save(ahe).getId(); + authHolderOldToNewIdMap.put(currentId, newId); + logger.debug("Read authentication holder {}", currentId); + } + reader.endArray(); + logger.info("Done reading authentication holders"); + } + + /** + * @param reader + * @return + * @throws IOException + */ + private SavedUserAuthentication readSavedUserAuthentication(JsonReader reader) throws IOException { + SavedUserAuthentication savedUserAuth = new SavedUserAuthentication(); + reader.beginObject(); + + while (reader.hasNext()) { + switch(reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(NAME)) { + savedUserAuth.setName(reader.nextString()); + } else if (name.equals(SOURCE_CLASS)) { + savedUserAuth.setSourceClass(reader.nextString()); + } else if (name.equals(AUTHENTICATED)) { + savedUserAuth.setAuthenticated(reader.nextBoolean()); + } else if (name.equals(AUTHORITIES)) { + Set authorityStrs = readSet(reader); + Set authorities = new HashSet(); + for (String s : authorityStrs) { + GrantedAuthority ga = new SimpleGrantedAuthority(s); + authorities.add(ga); + } + savedUserAuth.setAuthorities(authorities); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + + reader.endObject(); + return savedUserAuth; + } + + Map grantOldToNewIdMap = new HashMap<>(); + Map> grantToAccessTokensRefs = new HashMap<>(); + + /** + * @param reader + * @throws IOException + */ + private void readGrants(JsonReader reader) throws IOException { + reader.beginArray(); + while (reader.hasNext()) { + ApprovedSite site = new ApprovedSite(); + Long currentId = null; + Set tokenIds = null; + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ID)) { + currentId = reader.nextLong(); + } else if (name.equals(ACCESS_DATE)) { + Date date = utcToDate(reader.nextString()); + site.setAccessDate(date); + } else if (name.equals(CLIENT_ID)) { + site.setClientId(reader.nextString()); + } else if (name.equals(CREATION_DATE)) { + Date date = utcToDate(reader.nextString()); + site.setCreationDate(date); + } else if (name.equals(TIMEOUT_DATE)) { + Date date = utcToDate(reader.nextString()); + site.setTimeoutDate(date); + } else if (name.equals(USER_ID)) { + site.setUserId(reader.nextString()); + } else if (name.equals(ALLOWED_SCOPES)) { + Set allowedScopes = readSet(reader); + site.setAllowedScopes(allowedScopes); + } else if (name.equals(APPROVED_ACCESS_TOKENS)) { + tokenIds = readSet(reader); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + Long newId = approvedSiteRepository.save(site).getId(); + grantOldToNewIdMap.put(currentId, newId); + if (tokenIds != null) { + grantToAccessTokensRefs.put(currentId, tokenIds); + } + logger.debug("Read grant {}", currentId); + } + reader.endArray(); + logger.info("Done reading grants"); + } + Map whitelistedSiteOldToNewIdMap = new HashMap(); + + /** + * @param reader + * @throws IOException + */ + private void readWhitelistedSites(JsonReader reader) throws IOException { + reader.beginArray(); + while (reader.hasNext()) { + WhitelistedSite wlSite = new WhitelistedSite(); + Long currentId = null; + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (name.equals(ID)) { + currentId = reader.nextLong(); + } else if (name.equals(CLIENT_ID)) { + wlSite.setClientId(reader.nextString()); + } else if (name.equals(CREATOR_USER_ID)) { + wlSite.setCreatorUserId(reader.nextString()); + } else if (name.equals(ALLOWED_SCOPES)) { + Set allowedScopes = readSet(reader); + wlSite.setAllowedScopes(allowedScopes); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + Long newId = wlSiteRepository.save(wlSite).getId(); + whitelistedSiteOldToNewIdMap.put(currentId, newId); + } + reader.endArray(); + logger.info("Done reading whitelisted sites"); + } + + /** + * @param reader + * @throws IOException + */ + private void readBlacklistedSites(JsonReader reader) throws IOException { + reader.beginArray(); + while (reader.hasNext()) { + BlacklistedSite blSite = new BlacklistedSite(); + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (name.equals(ID)) { + reader.skipValue(); + } else if (name.equals(URI)) { + blSite.setUri(reader.nextString()); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + blSiteRepository.save(blSite); + } + reader.endArray(); + logger.info("Done reading blacklisted sites"); + } + + /** + * @param reader + * @throws IOException + */ + private void readClients(JsonReader reader) throws IOException { + reader.beginArray(); + while (reader.hasNext()) { + ClientDetailsEntity client = new ClientDetailsEntity(); + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(CLIENT_ID)) { + client.setClientId(reader.nextString()); + } else if (name.equals(RESOURCE_IDS)) { + Set resourceIds = readSet(reader); + client.setResourceIds(resourceIds); + } else if (name.equals(SECRET)) { + client.setClientSecret(reader.nextString()); + } else if (name.equals(SCOPE)) { + Set scope = readSet(reader); + client.setScope(scope); + } else if (name.equals(AUTHORITIES)) { + Set authorityStrs = readSet(reader); + Set authorities = new HashSet(); + for (String s : authorityStrs) { + GrantedAuthority ga = new SimpleGrantedAuthority(s); + authorities.add(ga); + } + client.setAuthorities(authorities); + } else if (name.equals(ACCESS_TOKEN_VALIDITY_SECONDS)) { + client.setAccessTokenValiditySeconds(reader.nextInt()); + } else if (name.equals(REFRESH_TOKEN_VALIDITY_SECONDS)) { + client.setRefreshTokenValiditySeconds(reader.nextInt()); + } else if (name.equals(REDIRECT_URIS)) { + Set redirectUris = readSet(reader); + client.setRedirectUris(redirectUris); + } else if (name.equals(CLAIMS_REDIRECT_URIS)) { + Set claimsRedirectUris = readSet(reader); + client.setClaimsRedirectUris(claimsRedirectUris); + } else if (name.equals(NAME)) { + client.setClientName(reader.nextString()); + } else if (name.equals(URI)) { + client.setClientUri(reader.nextString()); + } else if (name.equals(LOGO_URI)) { + client.setLogoUri(reader.nextString()); + } else if (name.equals(CONTACTS)) { + Set contacts = readSet(reader); + client.setContacts(contacts); + } else if (name.equals(TOS_URI)) { + client.setTosUri(reader.nextString()); + } else if (name.equals(TOKEN_ENDPOINT_AUTH_METHOD)) { + AuthMethod am = AuthMethod.getByValue(reader.nextString()); + client.setTokenEndpointAuthMethod(am); + } else if (name.equals(GRANT_TYPES)) { + Set grantTypes = readSet(reader); + client.setGrantTypes(grantTypes); + } else if (name.equals(RESPONSE_TYPES)) { + Set responseTypes = readSet(reader); + client.setResponseTypes(responseTypes); + } else if (name.equals(POLICY_URI)) { + client.setPolicyUri(reader.nextString()); + } else if (name.equals(APPLICATION_TYPE)) { + AppType appType = AppType.getByValue(reader.nextString()); + client.setApplicationType(appType); + } else if (name.equals(SECTOR_IDENTIFIER_URI)) { + client.setSectorIdentifierUri(reader.nextString()); + } else if (name.equals(SUBJECT_TYPE)) { + SubjectType st = SubjectType.getByValue(reader.nextString()); + client.setSubjectType(st); + } else if (name.equals(JWKS_URI)) { + client.setJwksUri(reader.nextString()); + } else if (name.equals(JWKS)) { + try { + client.setJwks(JWKSet.parse(reader.nextString())); + } catch (ParseException e) { + logger.error("Couldn't parse JWK Set", e); + } + } else if (name.equals(REQUEST_OBJECT_SIGNING_ALG)) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setRequestObjectSigningAlg(alg); + } else if (name.equals(USER_INFO_ENCRYPTED_RESPONSE_ALG)) { + JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); + client.setUserInfoEncryptedResponseAlg(alg); + } else if (name.equals(USER_INFO_ENCRYPTED_RESPONSE_ENC)) { + EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); + client.setUserInfoEncryptedResponseEnc(alg); + } else if (name.equals(USER_INFO_SIGNED_RESPONSE_ALG)) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setUserInfoSignedResponseAlg(alg); + } else if (name.equals(ID_TOKEN_SIGNED_RESPONSE_ALG)) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setIdTokenSignedResponseAlg(alg); + } else if (name.equals(ID_TOKEN_ENCRYPTED_RESPONSE_ALG)) { + JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); + client.setIdTokenEncryptedResponseAlg(alg); + } else if (name.equals(ID_TOKEN_ENCRYPTED_RESPONSE_ENC)) { + EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); + client.setIdTokenEncryptedResponseEnc(alg); + } else if (name.equals(TOKEN_ENDPOINT_AUTH_SIGNING_ALG)) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setTokenEndpointAuthSigningAlg(alg); + } else if (name.equals(DEFAULT_MAX_AGE)) { + client.setDefaultMaxAge(reader.nextInt()); + } else if (name.equals(REQUIRE_AUTH_TIME)) { + client.setRequireAuthTime(reader.nextBoolean()); + } else if (name.equals(DEFAULT_ACR_VALUES)) { + Set defaultACRvalues = readSet(reader); + client.setDefaultACRvalues(defaultACRvalues); + } else if (name.equals("initiateLoginUri")) { + client.setInitiateLoginUri(reader.nextString()); + } else if (name.equals(POST_LOGOUT_REDIRECT_URI)) { + Set postLogoutUris = readSet(reader); + client.setPostLogoutRedirectUris(postLogoutUris); + } else if (name.equals(REQUEST_URIS)) { + Set requestUris = readSet(reader); + client.setRequestUris(requestUris); + } else if (name.equals(DESCRIPTION)) { + client.setClientDescription(reader.nextString()); + } else if (name.equals(ALLOW_INTROSPECTION)) { + client.setAllowIntrospection(reader.nextBoolean()); + } else if (name.equals(REUSE_REFRESH_TOKEN)) { + client.setReuseRefreshToken(reader.nextBoolean()); + } else if (name.equals(CLEAR_ACCESS_TOKENS_ON_REFRESH)) { + client.setClearAccessTokensOnRefresh(reader.nextBoolean()); + } else if (name.equals(DYNAMICALLY_REGISTERED)) { + client.setDynamicallyRegistered(reader.nextBoolean()); + } else if (name.equals(CODE_CHALLENGE_METHOD)) { + client.setCodeChallengeMethod(PKCEAlgorithm.parse(reader.nextString())); + } else if (name.equals(SOFTWARE_STATEMENT)) { + try { + client.setSoftwareStatement(JWTParser.parse(reader.nextString())); + } catch (ParseException e) { + logger.error("Couldn't parse software statement", e); + } + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + clientRepository.saveClient(client); + } + reader.endArray(); + logger.info("Done reading clients"); + } + + /** + * Read the list of system scopes from the reader and insert them into the + * scope repository. + * + * @param reader + * @throws IOException + */ + private void readSystemScopes(JsonReader reader) throws IOException { + reader.beginArray(); + while (reader.hasNext()) { + SystemScope scope = new SystemScope(); + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(VALUE)) { + scope.setValue(reader.nextString()); + } else if (name.equals(DESCRIPTION)) { + scope.setDescription(reader.nextString()); + } else if (name.equals(RESTRICTED)) { + scope.setRestricted(reader.nextBoolean()); + } else if (name.equals(DEFAULT_SCOPE)) { + scope.setDefaultScope(reader.nextBoolean()); + } else if (name.equals(ICON)) { + scope.setIcon(reader.nextString()); + } else if (name.equals(STRUCTURED)) { + scope.setStructured(reader.nextBoolean()); + } else if (name.equals(STRUCTURED_PARAMETER)) { + scope.setStructuredParamDescription(reader.nextString()); + } else { + logger.debug("found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + sysScopeRepository.save(scope); + } + reader.endArray(); + logger.info("Done reading system scopes"); + } + + private void fixObjectReferences() { + logger.info("Fixing object references..."); + for (Long oldRefreshTokenId : refreshTokenToClientRefs.keySet()) { + String clientRef = refreshTokenToClientRefs.get(oldRefreshTokenId); + ClientDetailsEntity client = clientRepository.getClientByClientId(clientRef); + Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); + OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); + refreshToken.setClient(client); + tokenRepository.saveRefreshToken(refreshToken); + } + refreshTokenToClientRefs.clear(); + for (Long oldRefreshTokenId : refreshTokenToAuthHolderRefs.keySet()) { + Long oldAuthHolderId = refreshTokenToAuthHolderRefs.get(oldRefreshTokenId); + Long newAuthHolderId = authHolderOldToNewIdMap.get(oldAuthHolderId); + AuthenticationHolderEntity authHolder = authHolderRepository.getById(newAuthHolderId); + Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); + OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); + refreshToken.setAuthenticationHolder(authHolder); + tokenRepository.saveRefreshToken(refreshToken); + } + refreshTokenToAuthHolderRefs.clear(); + for (Long oldAccessTokenId : accessTokenToClientRefs.keySet()) { + String clientRef = accessTokenToClientRefs.get(oldAccessTokenId); + ClientDetailsEntity client = clientRepository.getClientByClientId(clientRef); + Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); + accessToken.setClient(client); + tokenRepository.saveAccessToken(accessToken); + } + accessTokenToClientRefs.clear(); + for (Long oldAccessTokenId : accessTokenToAuthHolderRefs.keySet()) { + Long oldAuthHolderId = accessTokenToAuthHolderRefs.get(oldAccessTokenId); + Long newAuthHolderId = authHolderOldToNewIdMap.get(oldAuthHolderId); + AuthenticationHolderEntity authHolder = authHolderRepository.getById(newAuthHolderId); + Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); + accessToken.setAuthenticationHolder(authHolder); + tokenRepository.saveAccessToken(accessToken); + } + accessTokenToAuthHolderRefs.clear(); + for (Long oldAccessTokenId : accessTokenToRefreshTokenRefs.keySet()) { + Long oldRefreshTokenId = accessTokenToRefreshTokenRefs.get(oldAccessTokenId); + Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); + OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); + Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); + accessToken.setRefreshToken(refreshToken); + tokenRepository.saveAccessToken(accessToken); + } + accessTokenToRefreshTokenRefs.clear(); + refreshTokenOldToNewIdMap.clear(); + for (Long oldAccessTokenId : accessTokenToIdTokenRefs.keySet()) { + Long oldIdTokenId = accessTokenToIdTokenRefs.get(oldAccessTokenId); + Long newIdTokenId = accessTokenOldToNewIdMap.get(oldIdTokenId); + OAuth2AccessTokenEntity idToken = tokenRepository.getAccessTokenById(newIdTokenId); + Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); + accessToken.setIdToken(idToken); + tokenRepository.saveAccessToken(accessToken); + } + accessTokenToIdTokenRefs.clear(); + for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { + Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); + Set tokens = new HashSet(); + for(Long oldTokenId : oldAccessTokenIds) { + Long newTokenId = accessTokenOldToNewIdMap.get(oldTokenId); + tokens.add(tokenRepository.getAccessTokenById(newTokenId)); + } + Long newGrantId = grantOldToNewIdMap.get(oldGrantId); + ApprovedSite site = approvedSiteRepository.getById(newGrantId); + site.setApprovedAccessTokens(tokens); + approvedSiteRepository.save(site); + } + accessTokenOldToNewIdMap.clear(); + grantOldToNewIdMap.clear(); + logger.info("Done fixing object references."); + } + +} diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java index 509ccc60c5..15a78c04ad 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java @@ -29,6 +29,7 @@ import org.mitre.openid.connect.service.impl.MITREidDataService_1_0; import org.mitre.openid.connect.service.impl.MITREidDataService_1_1; import org.mitre.openid.connect.service.impl.MITREidDataService_1_2; +import org.mitre.openid.connect.service.impl.MITREidDataService_1_3; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -75,6 +76,9 @@ public class DataAPI { @Autowired private MITREidDataService_1_2 dataService_1_2; + + @Autowired + private MITREidDataService_1_3 dataService_1_3; @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) public String importData(Reader in, Model m) throws IOException { @@ -94,6 +98,8 @@ public String importData(Reader in, Model m) throws IOException { dataService_1_1.importData(reader); } else if (name.equals(MITREidDataService.MITREID_CONNECT_1_2)) { dataService_1_2.importData(reader); + } else if (name.equals(MITREidDataService.MITREID_CONNECT_1_3)) { + dataService_1_3.importData(reader); } else { // consume the next bit silently for now logger.debug("Skipping value for " + name); // TODO: write these out? @@ -134,7 +140,7 @@ public void exportData(HttpServletResponse resp, Principal prin) throws IOExcept writer.value(prin.getName()); // delegate to the service to do the actual export - dataService_1_2.exportData(writer); + dataService_1_3.exportData(writer); writer.endObject(); // end root writer.close(); diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java index e9b612f014..7073671cca 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java @@ -149,121 +149,6 @@ public void prepare() { Mockito.reset(clientRepository, approvedSiteRepository, authHolderRepository, tokenRepository, sysScopeRepository, wlSiteRepository, blSiteRepository); } - @Test - public void testExportRefreshTokens() throws IOException, ParseException { - String expiration1 = "2014-09-10T22:49:44.090+0000"; - Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); - - ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); - when(mockedClient1.getClientId()).thenReturn("mocked_client_1"); - - AuthenticationHolderEntity mockedAuthHolder1 = mock(AuthenticationHolderEntity.class); - when(mockedAuthHolder1.getId()).thenReturn(1L); - - OAuth2RefreshTokenEntity token1 = new OAuth2RefreshTokenEntity(); - token1.setId(1L); - token1.setClient(mockedClient1); - token1.setExpiration(expirationDate1); - token1.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.")); - token1.setAuthenticationHolder(mockedAuthHolder1); - - String expiration2 = "2015-01-07T18:31:50.079+0000"; - Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); - - ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); - when(mockedClient2.getClientId()).thenReturn("mocked_client_2"); - - AuthenticationHolderEntity mockedAuthHolder2 = mock(AuthenticationHolderEntity.class); - when(mockedAuthHolder2.getId()).thenReturn(2L); - - OAuth2RefreshTokenEntity token2 = new OAuth2RefreshTokenEntity(); - token2.setId(2L); - token2.setClient(mockedClient2); - token2.setExpiration(expirationDate2); - token2.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJlYmEyYjc3My0xNjAzLTRmNDAtOWQ3MS1hMGIxZDg1OWE2MDAifQ.")); - token2.setAuthenticationHolder(mockedAuthHolder2); - - Set allRefreshTokens = ImmutableSet.of(token1, token2); - - Mockito.when(clientRepository.getAllClients()).thenReturn(new HashSet()); - Mockito.when(approvedSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(wlSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(blSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(authHolderRepository.getAll()).thenReturn(new ArrayList()); - Mockito.when(tokenRepository.getAllAccessTokens()).thenReturn(new HashSet()); - Mockito.when(tokenRepository.getAllRefreshTokens()).thenReturn(allRefreshTokens); - Mockito.when(sysScopeRepository.getAll()).thenReturn(new HashSet()); - - // do the data export - StringWriter stringWriter = new StringWriter(); - JsonWriter writer = new JsonWriter(stringWriter); - writer.beginObject(); - dataService.exportData(writer); - writer.endObject(); - writer.close(); - - // parse the output as a JSON object for testing - JsonElement elem = new JsonParser().parse(stringWriter.toString()); - JsonObject root = elem.getAsJsonObject(); - - // make sure the root is there - assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_2), is(true)); - - JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_2).getAsJsonObject(); - - // make sure all the root elements are there - assertThat(config.has(MITREidDataService.CLIENTS), is(true)); - assertThat(config.has(MITREidDataService.GRANTS), is(true)); - assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); - assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); - assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); - assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); - - // make sure the root elements are all arrays - assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); - - - // check our refresh token list (this test) - JsonArray refreshTokens = config.get(MITREidDataService.REFRESHTOKENS).getAsJsonArray(); - - assertThat(refreshTokens.size(), is(2)); - // check for both of our refresh tokens in turn - Set checked = new HashSet<>(); - for (JsonElement e : refreshTokens) { - assertThat(e.isJsonObject(), is(true)); - JsonObject token = e.getAsJsonObject(); - - OAuth2RefreshTokenEntity compare = null; - if (token.get("id").getAsLong() == token1.getId()) { - compare = token1; - } else if (token.get("id").getAsLong() == token2.getId()) { - compare = token2; - } - - if (compare == null) { - fail("Could not find matching id: " + token.get("id").getAsString()); - } else { - assertThat(token.get("id").getAsLong(), equalTo(compare.getId())); - assertThat(token.get("clientId").getAsString(), equalTo(compare.getClient().getClientId())); - assertThat(token.get("expiration").getAsString(), equalTo(formatter.print(compare.getExpiration(), Locale.ENGLISH))); - assertThat(token.get("value").getAsString(), equalTo(compare.getValue())); - assertThat(token.get("authenticationHolderId").getAsLong(), equalTo(compare.getAuthenticationHolder().getId())); - checked.add(compare); - } - } - // make sure all of our refresh tokens were found - assertThat(checked.containsAll(allRefreshTokens), is(true)); - } - private class refreshTokenIdComparator implements Comparator { @Override public int compare(OAuth2RefreshTokenEntity entity1, OAuth2RefreshTokenEntity entity2) { @@ -384,143 +269,6 @@ public AuthenticationHolderEntity answer(InvocationOnMock invocation) throws Thr assertThat(savedRefreshTokens.get(1).getValue(), equalTo(token2.getValue())); } - @Test - public void testExportAccessTokens() throws IOException, ParseException { - String expiration1 = "2014-09-10T22:49:44.090+0000"; - Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); - - ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); - when(mockedClient1.getClientId()).thenReturn("mocked_client_1"); - - AuthenticationHolderEntity mockedAuthHolder1 = mock(AuthenticationHolderEntity.class); - when(mockedAuthHolder1.getId()).thenReturn(1L); - - OAuth2AccessTokenEntity token1 = new OAuth2AccessTokenEntity(); - token1.setId(1L); - token1.setClient(mockedClient1); - token1.setExpiration(expirationDate1); - token1.setJwt(JWTParser.parse("eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3ODk5NjgsInN1YiI6IjkwMzQyLkFTREZKV0ZBIiwiYXRfaGFzaCI6InptTmt1QmNRSmNYQktNaVpFODZqY0EiLCJhdWQiOlsiY2xpZW50Il0sImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdDo4MDgwXC9vcGVuaWQtY29ubmVjdC1zZXJ2ZXItd2ViYXBwXC8iLCJpYXQiOjE0MTI3ODkzNjh9.xkEJ9IMXpH7qybWXomfq9WOOlpGYnrvGPgey9UQ4GLzbQx7JC0XgJK83PmrmBZosvFPCmota7FzI_BtwoZLgAZfFiH6w3WIlxuogoH-TxmYbxEpTHoTsszZppkq9mNgOlArV4jrR9y3TPo4MovsH71dDhS_ck-CvAlJunHlqhs0")); - token1.setAuthenticationHolder(mockedAuthHolder1); - token1.setScope(ImmutableSet.of("id-token")); - token1.setTokenType("Bearer"); - - String expiration2 = "2015-01-07T18:31:50.079+0000"; - Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); - - ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); - when(mockedClient2.getClientId()).thenReturn("mocked_client_2"); - - AuthenticationHolderEntity mockedAuthHolder2 = mock(AuthenticationHolderEntity.class); - when(mockedAuthHolder2.getId()).thenReturn(2L); - - OAuth2RefreshTokenEntity mockRefreshToken2 = mock(OAuth2RefreshTokenEntity.class); - when(mockRefreshToken2.getId()).thenReturn(1L); - - OAuth2AccessTokenEntity token2 = new OAuth2AccessTokenEntity(); - token2.setId(2L); - token2.setClient(mockedClient2); - token2.setExpiration(expirationDate2); - token2.setJwt(JWTParser.parse("eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3OTI5NjgsImF1ZCI6WyJjbGllbnQiXSwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwODBcL29wZW5pZC1jb25uZWN0LXNlcnZlci13ZWJhcHBcLyIsImp0aSI6IjBmZGE5ZmRiLTYyYzItNGIzZS05OTdiLWU0M2VhMDUwMzNiOSIsImlhdCI6MTQxMjc4OTM2OH0.xgaVpRLYE5MzbgXfE0tZt823tjAm6Oh3_kdR1P2I9jRLR6gnTlBQFlYi3Y_0pWNnZSerbAE8Tn6SJHZ9k-curVG0-ByKichV7CNvgsE5X_2wpEaUzejvKf8eZ-BammRY-ie6yxSkAarcUGMvGGOLbkFcz5CtrBpZhfd75J49BIQ")); - token2.setAuthenticationHolder(mockedAuthHolder2); - token2.setIdToken(token1); - token2.setRefreshToken(mockRefreshToken2); - token2.setScope(ImmutableSet.of("openid", "offline_access", "email", "profile")); - token2.setTokenType("Bearer"); - - Set allAccessTokens = ImmutableSet.of(token1, token2); - - Mockito.when(clientRepository.getAllClients()).thenReturn(new HashSet()); - Mockito.when(approvedSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(wlSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(blSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(authHolderRepository.getAll()).thenReturn(new ArrayList()); - Mockito.when(tokenRepository.getAllRefreshTokens()).thenReturn(new HashSet()); - Mockito.when(tokenRepository.getAllAccessTokens()).thenReturn(allAccessTokens); - Mockito.when(sysScopeRepository.getAll()).thenReturn(new HashSet()); - - // do the data export - StringWriter stringWriter = new StringWriter(); - JsonWriter writer = new JsonWriter(stringWriter); - writer.beginObject(); - dataService.exportData(writer); - writer.endObject(); - writer.close(); - - // parse the output as a JSON object for testing - JsonElement elem = new JsonParser().parse(stringWriter.toString()); - JsonObject root = elem.getAsJsonObject(); - - // make sure the root is there - assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_2), is(true)); - - JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_2).getAsJsonObject(); - - // make sure all the root elements are there - assertThat(config.has(MITREidDataService.CLIENTS), is(true)); - assertThat(config.has(MITREidDataService.GRANTS), is(true)); - assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); - assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); - assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); - assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); - - // make sure the root elements are all arrays - assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); - - - // check our access token list (this test) - JsonArray accessTokens = config.get(MITREidDataService.ACCESSTOKENS).getAsJsonArray(); - - assertThat(accessTokens.size(), is(2)); - // check for both of our access tokens in turn - Set checked = new HashSet<>(); - for (JsonElement e : accessTokens) { - assertTrue(e.isJsonObject()); - JsonObject token = e.getAsJsonObject(); - - OAuth2AccessTokenEntity compare = null; - if (token.get("id").getAsLong() == token1.getId().longValue()) { - compare = token1; - } else if (token.get("id").getAsLong() == token2.getId().longValue()) { - compare = token2; - } - - if (compare == null) { - fail("Could not find matching id: " + token.get("id").getAsString()); - } else { - assertThat(token.get("id").getAsLong(), equalTo(compare.getId())); - assertThat(token.get("clientId").getAsString(), equalTo(compare.getClient().getClientId())); - assertThat(token.get("expiration").getAsString(), equalTo(formatter.print(compare.getExpiration(), Locale.ENGLISH))); - assertThat(token.get("value").getAsString(), equalTo(compare.getValue())); - assertThat(token.get("type").getAsString(), equalTo(compare.getTokenType())); - assertThat(token.get("authenticationHolderId").getAsLong(), equalTo(compare.getAuthenticationHolder().getId())); - assertTrue(token.get("scope").isJsonArray()); - assertThat(jsonArrayToStringSet(token.getAsJsonArray("scope")), equalTo(compare.getScope())); - if(token.get("idTokenId").isJsonNull()) { - assertNull(compare.getIdToken()); - } else { - assertThat(token.get("idTokenId").getAsLong(), equalTo(compare.getIdToken().getId())); - } - if(token.get("refreshTokenId").isJsonNull()) { - assertNull(compare.getIdToken()); - } else { - assertThat(token.get("refreshTokenId").getAsLong(), equalTo(compare.getRefreshToken().getId())); - } - checked.add(compare); - } - } - // make sure all of our access tokens were found - assertThat(checked.containsAll(allAccessTokens), is(true)); - } - private class accessTokenIdComparator implements Comparator { @Override public int compare(OAuth2AccessTokenEntity entity1, OAuth2AccessTokenEntity entity2) { @@ -653,111 +401,6 @@ public AuthenticationHolderEntity answer(InvocationOnMock invocation) throws Thr assertThat(savedAccessTokens.get(1).getValue(), equalTo(token2.getValue())); } - @Test - public void testExportClients() throws IOException { - ClientDetailsEntity client1 = new ClientDetailsEntity(); - client1.setId(1L); - client1.setAccessTokenValiditySeconds(3600); - client1.setClientId("client1"); - client1.setClientSecret("clientsecret1"); - client1.setRedirectUris(ImmutableSet.of("http://foo.com/")); - client1.setScope(ImmutableSet.of("foo", "bar", "baz", "dolphin")); - client1.setGrantTypes(ImmutableSet.of("implicit", "authorization_code", "urn:ietf:params:oauth:grant_type:redelegate", "refresh_token")); - client1.setAllowIntrospection(true); - - ClientDetailsEntity client2 = new ClientDetailsEntity(); - client2.setId(2L); - client2.setAccessTokenValiditySeconds(3600); - client2.setClientId("client2"); - client2.setClientSecret("clientsecret2"); - client2.setRedirectUris(ImmutableSet.of("http://bar.baz.com/")); - client2.setScope(ImmutableSet.of("foo", "dolphin", "electric-wombat")); - client2.setGrantTypes(ImmutableSet.of("client_credentials", "urn:ietf:params:oauth:grant_type:redelegate")); - client2.setAllowIntrospection(false); - - Set allClients = ImmutableSet.of(client1, client2); - - Mockito.when(clientRepository.getAllClients()).thenReturn(allClients); - Mockito.when(approvedSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(wlSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(blSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(authHolderRepository.getAll()).thenReturn(new ArrayList()); - Mockito.when(tokenRepository.getAllAccessTokens()).thenReturn(new HashSet()); - Mockito.when(tokenRepository.getAllRefreshTokens()).thenReturn(new HashSet()); - Mockito.when(sysScopeRepository.getAll()).thenReturn(new HashSet()); - - // do the data export - StringWriter stringWriter = new StringWriter(); - JsonWriter writer = new JsonWriter(stringWriter); - writer.beginObject(); - dataService.exportData(writer); - writer.endObject(); - writer.close(); - - // parse the output as a JSON object for testing - JsonElement elem = new JsonParser().parse(stringWriter.toString()); - JsonObject root = elem.getAsJsonObject(); - - // make sure the root is there - assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_2), is(true)); - - JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_2).getAsJsonObject(); - - // make sure all the root elements are there - assertThat(config.has(MITREidDataService.CLIENTS), is(true)); - assertThat(config.has(MITREidDataService.GRANTS), is(true)); - assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); - assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); - assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); - assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); - - // make sure the root elements are all arrays - assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); - - - // check our client list (this test) - JsonArray clients = config.get(MITREidDataService.CLIENTS).getAsJsonArray(); - - assertThat(clients.size(), is(2)); - // check for both of our clients in turn - Set checked = new HashSet<>(); - for (JsonElement e : clients) { - assertThat(e.isJsonObject(), is(true)); - JsonObject client = e.getAsJsonObject(); - - ClientDetailsEntity compare = null; - if (client.get("clientId").getAsString().equals(client1.getClientId())) { - compare = client1; - } else if (client.get("clientId").getAsString().equals(client2.getClientId())) { - compare = client2; - } - - if (compare == null) { - fail("Could not find matching clientId: " + client.get("clientId").getAsString()); - } else { - assertThat(client.get("clientId").getAsString(), equalTo(compare.getClientId())); - assertThat(client.get("secret").getAsString(), equalTo(compare.getClientSecret())); - assertThat(client.get("accessTokenValiditySeconds").getAsInt(), equalTo(compare.getAccessTokenValiditySeconds())); - assertThat(client.get("allowIntrospection").getAsBoolean(), equalTo(compare.isAllowIntrospection())); - assertThat(jsonArrayToStringSet(client.get("redirectUris").getAsJsonArray()), equalTo(compare.getRedirectUris())); - assertThat(jsonArrayToStringSet(client.get("scope").getAsJsonArray()), equalTo(compare.getScope())); - assertThat(jsonArrayToStringSet(client.get("grantTypes").getAsJsonArray()), equalTo(compare.getGrantTypes())); - checked.add(compare); - } - } - // make sure all of our clients were found - assertThat(checked.containsAll(allClients), is(true)); - } - @Test public void testImportClients() throws IOException { ClientDetailsEntity client1 = new ClientDetailsEntity(); @@ -832,99 +475,6 @@ public void testImportClients() throws IOException { assertThat(savedClients.get(1).isAllowIntrospection(), equalTo(client2.isAllowIntrospection())); } - @Test - public void testExportBlacklistedSites() throws IOException { - BlacklistedSite site1 = new BlacklistedSite(); - site1.setId(1L); - site1.setUri("http://foo.com"); - - BlacklistedSite site2 = new BlacklistedSite(); - site2.setId(2L); - site2.setUri("http://bar.com"); - - BlacklistedSite site3 = new BlacklistedSite(); - site3.setId(3L); - site3.setUri("http://baz.com"); - - Set allBlacklistedSites = ImmutableSet.of(site1, site2, site3); - - Mockito.when(clientRepository.getAllClients()).thenReturn(new HashSet()); - Mockito.when(approvedSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(wlSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(blSiteRepository.getAll()).thenReturn(allBlacklistedSites); - Mockito.when(authHolderRepository.getAll()).thenReturn(new ArrayList()); - Mockito.when(tokenRepository.getAllAccessTokens()).thenReturn(new HashSet()); - Mockito.when(tokenRepository.getAllRefreshTokens()).thenReturn(new HashSet()); - Mockito.when(sysScopeRepository.getAll()).thenReturn(new HashSet()); - - // do the data export - StringWriter stringWriter = new StringWriter(); - JsonWriter writer = new JsonWriter(stringWriter); - writer.beginObject(); - dataService.exportData(writer); - writer.endObject(); - writer.close(); - - // parse the output as a JSON object for testing - JsonElement elem = new JsonParser().parse(stringWriter.toString()); - JsonObject root = elem.getAsJsonObject(); - - // make sure the root is there - assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_2), is(true)); - - JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_2).getAsJsonObject(); - - // make sure all the root elements are there - assertThat(config.has(MITREidDataService.CLIENTS), is(true)); - assertThat(config.has(MITREidDataService.GRANTS), is(true)); - assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); - assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); - assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); - assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); - - // make sure the root elements are all arrays - assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); - - // check our scope list (this test) - JsonArray sites = config.get(MITREidDataService.BLACKLISTEDSITES).getAsJsonArray(); - - assertThat(sites.size(), is(3)); - // check for both of our sites in turn - Set checked = new HashSet<>(); - for (JsonElement e : sites) { - assertThat(e.isJsonObject(), is(true)); - JsonObject site = e.getAsJsonObject(); - - BlacklistedSite compare = null; - if (site.get("id").getAsLong() == site1.getId().longValue()) { - compare = site1; - } else if (site.get("id").getAsLong() == site2.getId().longValue()) { - compare = site2; - } else if (site.get("id").getAsLong() == site3.getId().longValue()) { - compare = site3; - } - - if (compare == null) { - fail("Could not find matching blacklisted site id: " + site.get("id").getAsString()); - } else { - assertThat(site.get("uri").getAsString(), equalTo(compare.getUri())); - checked.add(compare); - } - } - // make sure all of our clients were found - assertThat(checked.containsAll(allBlacklistedSites), is(true)); - - } - @Test public void testImportBlacklistedSites() throws IOException { BlacklistedSite site1 = new BlacklistedSite(); @@ -973,99 +523,6 @@ public void testImportBlacklistedSites() throws IOException { assertThat(savedSites.get(2).getUri(), equalTo(site3.getUri())); } - @Test - public void testExportWhitelistedSites() throws IOException { - WhitelistedSite site1 = new WhitelistedSite(); - site1.setId(1L); - site1.setClientId("foo"); - - WhitelistedSite site2 = new WhitelistedSite(); - site2.setId(2L); - site2.setClientId("bar"); - - WhitelistedSite site3 = new WhitelistedSite(); - site3.setId(3L); - site3.setClientId("baz"); - - Set allWhitelistedSites = ImmutableSet.of(site1, site2, site3); - - Mockito.when(clientRepository.getAllClients()).thenReturn(new HashSet()); - Mockito.when(approvedSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(blSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(wlSiteRepository.getAll()).thenReturn(allWhitelistedSites); - Mockito.when(authHolderRepository.getAll()).thenReturn(new ArrayList()); - Mockito.when(tokenRepository.getAllAccessTokens()).thenReturn(new HashSet()); - Mockito.when(tokenRepository.getAllRefreshTokens()).thenReturn(new HashSet()); - Mockito.when(sysScopeRepository.getAll()).thenReturn(new HashSet()); - - // do the data export - StringWriter stringWriter = new StringWriter(); - JsonWriter writer = new JsonWriter(stringWriter); - writer.beginObject(); - dataService.exportData(writer); - writer.endObject(); - writer.close(); - - // parse the output as a JSON object for testing - JsonElement elem = new JsonParser().parse(stringWriter.toString()); - JsonObject root = elem.getAsJsonObject(); - - // make sure the root is there - assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_2), is(true)); - - JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_2).getAsJsonObject(); - - // make sure all the root elements are there - assertThat(config.has(MITREidDataService.CLIENTS), is(true)); - assertThat(config.has(MITREidDataService.GRANTS), is(true)); - assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); - assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); - assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); - assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); - - // make sure the root elements are all arrays - assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); - - // check our scope list (this test) - JsonArray sites = config.get(MITREidDataService.WHITELISTEDSITES).getAsJsonArray(); - - assertThat(sites.size(), is(3)); - // check for both of our sites in turn - Set checked = new HashSet<>(); - for (JsonElement e : sites) { - assertThat(e.isJsonObject(), is(true)); - JsonObject site = e.getAsJsonObject(); - - WhitelistedSite compare = null; - if (site.get("id").getAsLong() == site1.getId().longValue()) { - compare = site1; - } else if (site.get("id").getAsLong() == site2.getId().longValue()) { - compare = site2; - } else if (site.get("id").getAsLong() == site3.getId().longValue()) { - compare = site3; - } - - if (compare == null) { - fail("Could not find matching whitelisted site id: " + site.get("id").getAsString()); - } else { - assertThat(site.get("clientId").getAsString(), equalTo(compare.getClientId())); - checked.add(compare); - } - } - // make sure all of our clients were found - assertThat(checked.containsAll(allWhitelistedSites), is(true)); - - } - @Test public void testImportWhitelistedSites() throws IOException { WhitelistedSite site1 = new WhitelistedSite(); @@ -1135,131 +592,6 @@ public WhitelistedSite answer(InvocationOnMock invocation) throws Throwable { assertThat(savedSites.get(2).getClientId(), equalTo(site3.getClientId())); } - @Test - public void testExportGrants() throws IOException, ParseException { - Date creationDate1 = formatter.parse("2014-09-10T22:49:44.090+0000", Locale.ENGLISH); - Date accessDate1 = formatter.parse("2014-09-10T23:49:44.090+0000", Locale.ENGLISH); - - OAuth2AccessTokenEntity mockToken1 = mock(OAuth2AccessTokenEntity.class); - when(mockToken1.getId()).thenReturn(1L); - - ApprovedSite site1 = new ApprovedSite(); - site1.setId(1L); - site1.setClientId("foo"); - site1.setCreationDate(creationDate1); - site1.setAccessDate(accessDate1); - site1.setUserId("user1"); - site1.setAllowedScopes(ImmutableSet.of("openid", "phone")); - site1.setApprovedAccessTokens(ImmutableSet.of(mockToken1)); - - Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+0000", Locale.ENGLISH); - Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+0000", Locale.ENGLISH); - Date timeoutDate2 = formatter.parse("2014-10-01T20:49:44.090+0000", Locale.ENGLISH); - - ApprovedSite site2 = new ApprovedSite(); - site2.setId(2L); - site2.setClientId("bar"); - site2.setCreationDate(creationDate2); - site2.setAccessDate(accessDate2); - site2.setUserId("user2"); - site2.setAllowedScopes(ImmutableSet.of("openid", "offline_access", "email", "profile")); - site2.setTimeoutDate(timeoutDate2); - - Set allApprovedSites = ImmutableSet.of(site1, site2); - - Mockito.when(clientRepository.getAllClients()).thenReturn(new HashSet()); - Mockito.when(approvedSiteRepository.getAll()).thenReturn(allApprovedSites); - Mockito.when(blSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(wlSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(authHolderRepository.getAll()).thenReturn(new ArrayList()); - Mockito.when(tokenRepository.getAllAccessTokens()).thenReturn(new HashSet()); - Mockito.when(tokenRepository.getAllRefreshTokens()).thenReturn(new HashSet()); - Mockito.when(sysScopeRepository.getAll()).thenReturn(new HashSet()); - - // do the data export - StringWriter stringWriter = new StringWriter(); - JsonWriter writer = new JsonWriter(stringWriter); - writer.beginObject(); - dataService.exportData(writer); - writer.endObject(); - writer.close(); - - // parse the output as a JSON object for testing - JsonElement elem = new JsonParser().parse(stringWriter.toString()); - JsonObject root = elem.getAsJsonObject(); - - // make sure the root is there - assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_2), is(true)); - - JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_2).getAsJsonObject(); - - // make sure all the root elements are there - assertThat(config.has(MITREidDataService.CLIENTS), is(true)); - assertThat(config.has(MITREidDataService.GRANTS), is(true)); - assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); - assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); - assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); - assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); - - // make sure the root elements are all arrays - assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); - - // check our scope list (this test) - JsonArray sites = config.get(MITREidDataService.GRANTS).getAsJsonArray(); - - assertThat(sites.size(), is(2)); - // check for both of our sites in turn - Set checked = new HashSet<>(); - for (JsonElement e : sites) { - assertThat(e.isJsonObject(), is(true)); - JsonObject site = e.getAsJsonObject(); - - ApprovedSite compare = null; - if (site.get("id").getAsLong() == site1.getId().longValue()) { - compare = site1; - } else if (site.get("id").getAsLong() == site2.getId().longValue()) { - compare = site2; - } - - if (compare == null) { - fail("Could not find matching whitelisted site id: " + site.get("id").getAsString()); - } else { - assertThat(site.get("clientId").getAsString(), equalTo(compare.getClientId())); - assertThat(site.get("creationDate").getAsString(), equalTo(formatter.print(compare.getCreationDate(), Locale.ENGLISH))); - assertThat(site.get("accessDate").getAsString(), equalTo(formatter.print(compare.getAccessDate(), Locale.ENGLISH))); - if(site.get("timeoutDate").isJsonNull()) { - assertNull(compare.getTimeoutDate()); - } else { - assertThat(site.get("timeoutDate").getAsString(), equalTo(formatter.print(compare.getTimeoutDate(), Locale.ENGLISH))); - } - assertThat(site.get("userId").getAsString(), equalTo(compare.getUserId())); - assertThat(jsonArrayToStringSet(site.getAsJsonArray("allowedScopes")), equalTo(compare.getAllowedScopes())); - if (site.get("approvedAccessTokens").isJsonNull() || site.getAsJsonArray("approvedAccessTokens") == null) { - assertTrue(compare.getApprovedAccessTokens() == null || compare.getApprovedAccessTokens().isEmpty()); - } else { - assertNotNull(compare.getApprovedAccessTokens()); - Set tokenIds = new HashSet<>(); - for(OAuth2AccessTokenEntity entity : compare.getApprovedAccessTokens()) { - tokenIds.add(entity.getId().toString()); - } - assertThat(jsonArrayToStringSet(site.getAsJsonArray("approvedAccessTokens")), equalTo(tokenIds)); - } - checked.add(compare); - } - } - // make sure all of our clients were found - assertThat(checked.containsAll(allApprovedSites), is(true)); - } - @Test public void testImportGrants() throws IOException, ParseException { Date creationDate1 = formatter.parse("2014-09-10T22:49:44.090+0000", Locale.ENGLISH); @@ -1376,113 +708,6 @@ public OAuth2AccessTokenEntity answer(InvocationOnMock invocation) throws Throwa assertThat(savedSites.get(1).getApprovedAccessTokens().size(), equalTo(site2.getApprovedAccessTokens().size())); } - @Test - public void testExportAuthenticationHolders() throws IOException { - OAuth2Request req1 = new OAuth2Request(new HashMap(), "client1", new ArrayList(), - true, new HashSet(), new HashSet(), "http://foo.com", - new HashSet(), null); - Authentication mockAuth1 = new UsernamePasswordAuthenticationToken("user1", "pass1", AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); - OAuth2Authentication auth1 = new OAuth2Authentication(req1, mockAuth1); - - AuthenticationHolderEntity holder1 = new AuthenticationHolderEntity(); - holder1.setId(1L); - holder1.setAuthentication(auth1); - - OAuth2Request req2 = new OAuth2Request(new HashMap(), "client2", new ArrayList(), - true, new HashSet(), new HashSet(), "http://bar.com", - new HashSet(), null); - OAuth2Authentication auth2 = new OAuth2Authentication(req2, null); - - AuthenticationHolderEntity holder2 = new AuthenticationHolderEntity(); - holder2.setId(2L); - holder2.setAuthentication(auth2); - - List allAuthHolders = ImmutableList.of(holder1, holder2); - - when(clientRepository.getAllClients()).thenReturn(new HashSet()); - when(approvedSiteRepository.getAll()).thenReturn(new HashSet()); - when(wlSiteRepository.getAll()).thenReturn(new HashSet()); - when(blSiteRepository.getAll()).thenReturn(new HashSet()); - when(authHolderRepository.getAll()).thenReturn(allAuthHolders); - when(tokenRepository.getAllAccessTokens()).thenReturn(new HashSet()); - when(tokenRepository.getAllRefreshTokens()).thenReturn(new HashSet()); - when(sysScopeRepository.getAll()).thenReturn(new HashSet()); - - // do the data export - StringWriter stringWriter = new StringWriter(); - JsonWriter writer = new JsonWriter(stringWriter); - writer.beginObject(); - dataService.exportData(writer); - writer.endObject(); - writer.close(); - - // parse the output as a JSON object for testing - JsonElement elem = new JsonParser().parse(stringWriter.toString()); - JsonObject root = elem.getAsJsonObject(); - - // make sure the root is there - assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_2), is(true)); - - JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_2).getAsJsonObject(); - - // make sure all the root elements are there - assertThat(config.has(MITREidDataService.CLIENTS), is(true)); - assertThat(config.has(MITREidDataService.GRANTS), is(true)); - assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); - assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); - assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); - assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); - - // make sure the root elements are all arrays - assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); - - - // check our holder list (this test) - JsonArray holders = config.get(MITREidDataService.AUTHENTICATIONHOLDERS).getAsJsonArray(); - - assertThat(holders.size(), is(2)); - // check for both of our clients in turn - Set checked = new HashSet<>(); - for (JsonElement e : holders) { - assertThat(e.isJsonObject(), is(true)); - JsonObject holder = e.getAsJsonObject(); - - AuthenticationHolderEntity compare = null; - if (holder.get("id").getAsLong() == holder1.getId()) { - compare = holder1; - } else if (holder.get("id").getAsLong() == holder2.getId()) { - compare = holder2; - } - - if (compare == null) { - fail("Could not find matching authentication holder id: " + holder.get("id").getAsString()); - } else { - assertTrue(holder.get("clientId").getAsString().equals(compare.getClientId())); - assertTrue(holder.get("approved").getAsBoolean() == compare.isApproved()); - assertTrue(holder.get("redirectUri").getAsString().equals(compare.getRedirectUri())); - if (compare.getUserAuth() != null) { - assertTrue(holder.get("savedUserAuthentication").isJsonObject()); - JsonObject savedAuth = holder.get("savedUserAuthentication").getAsJsonObject(); - assertTrue(savedAuth.get("name").getAsString().equals(compare.getUserAuth().getName())); - assertTrue(savedAuth.get("authenticated").getAsBoolean() == compare.getUserAuth().isAuthenticated()); - assertTrue(savedAuth.get("sourceClass").getAsString().equals(compare.getUserAuth().getSourceClass())); - } - checked.add(compare); - } - } - // make sure all of our clients were found - assertThat(checked.containsAll(allAuthHolders), is(true)); - } - @Test public void testImportAuthenticationHolders() throws IOException { OAuth2Request req1 = new OAuth2Request(new HashMap(), "client1", new ArrayList(), @@ -1550,116 +775,6 @@ public AuthenticationHolderEntity answer(InvocationOnMock invocation) throws Thr assertThat(savedAuthHolders.get(1).getAuthentication().getOAuth2Request().getClientId(), equalTo(holder2.getAuthentication().getOAuth2Request().getClientId())); } - @Test - public void testExportSystemScopes() throws IOException { - SystemScope scope1 = new SystemScope(); - scope1.setId(1L); - scope1.setValue("scope1"); - scope1.setDescription("Scope 1"); - scope1.setRestricted(true); - scope1.setDefaultScope(false); - scope1.setIcon("glass"); - - SystemScope scope2 = new SystemScope(); - scope2.setId(2L); - scope2.setValue("scope2"); - scope2.setDescription("Scope 2"); - scope2.setRestricted(false); - scope2.setDefaultScope(false); - scope2.setIcon("ball"); - - SystemScope scope3 = new SystemScope(); - scope3.setId(3L); - scope3.setValue("scope3"); - scope3.setDescription("Scope 3"); - scope3.setRestricted(false); - scope3.setDefaultScope(true); - scope3.setIcon("road"); - - Set allScopes = ImmutableSet.of(scope1, scope2, scope3); - - Mockito.when(clientRepository.getAllClients()).thenReturn(new HashSet()); - Mockito.when(approvedSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(wlSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(blSiteRepository.getAll()).thenReturn(new HashSet()); - Mockito.when(authHolderRepository.getAll()).thenReturn(new ArrayList()); - Mockito.when(tokenRepository.getAllAccessTokens()).thenReturn(new HashSet()); - Mockito.when(tokenRepository.getAllRefreshTokens()).thenReturn(new HashSet()); - Mockito.when(sysScopeRepository.getAll()).thenReturn(allScopes); - - // do the data export - StringWriter stringWriter = new StringWriter(); - JsonWriter writer = new JsonWriter(stringWriter); - writer.beginObject(); - dataService.exportData(writer); - writer.endObject(); - writer.close(); - - // parse the output as a JSON object for testing - JsonElement elem = new JsonParser().parse(stringWriter.toString()); - JsonObject root = elem.getAsJsonObject(); - - // make sure the root is there - assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_2), is(true)); - - JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_2).getAsJsonObject(); - - // make sure all the root elements are there - assertThat(config.has(MITREidDataService.CLIENTS), is(true)); - assertThat(config.has(MITREidDataService.GRANTS), is(true)); - assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); - assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); - assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); - assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); - assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); - - // make sure the root elements are all arrays - assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); - assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); - - - // check our scope list (this test) - JsonArray scopes = config.get(MITREidDataService.SYSTEMSCOPES).getAsJsonArray(); - - assertThat(scopes.size(), is(3)); - // check for both of our clients in turn - Set checked = new HashSet<>(); - for (JsonElement e : scopes) { - assertThat(e.isJsonObject(), is(true)); - JsonObject scope = e.getAsJsonObject(); - - SystemScope compare = null; - if (scope.get("value").getAsString().equals(scope1.getValue())) { - compare = scope1; - } else if (scope.get("value").getAsString().equals(scope2.getValue())) { - compare = scope2; - } else if (scope.get("value").getAsString().equals(scope3.getValue())) { - compare = scope3; - } - - if (compare == null) { - fail("Could not find matching scope value: " + scope.get("value").getAsString()); - } else { - assertThat(scope.get("value").getAsString(), equalTo(compare.getValue())); - assertThat(scope.get("description").getAsString(), equalTo(compare.getDescription())); - assertThat(scope.get("icon").getAsString(), equalTo(compare.getIcon())); - assertThat(scope.get("restricted").getAsBoolean(), equalTo(compare.isRestricted())); - assertThat(scope.get("defaultScope").getAsBoolean(), equalTo(compare.isDefaultScope())); - checked.add(compare); - } - } - // make sure all of our clients were found - assertThat(checked.containsAll(allScopes), is(true)); - - } - @Test public void testImportSystemScopes() throws IOException { SystemScope scope1 = new SystemScope(); diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java new file mode 100644 index 0000000000..8ce49b7605 --- /dev/null +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java @@ -0,0 +1,1887 @@ +/******************************************************************************* + * Copyright 2016 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.openid.connect.service.impl; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mitre.oauth2.model.AuthenticationHolderEntity; +import org.mitre.oauth2.model.ClientDetailsEntity; +import org.mitre.oauth2.model.OAuth2AccessTokenEntity; +import org.mitre.oauth2.model.OAuth2RefreshTokenEntity; +import org.mitre.oauth2.model.PKCEAlgorithm; +import org.mitre.oauth2.model.SystemScope; +import org.mitre.oauth2.repository.AuthenticationHolderRepository; +import org.mitre.oauth2.repository.OAuth2ClientRepository; +import org.mitre.oauth2.repository.OAuth2TokenRepository; +import org.mitre.oauth2.repository.SystemScopeRepository; +import org.mitre.openid.connect.model.ApprovedSite; +import org.mitre.openid.connect.model.BlacklistedSite; +import org.mitre.openid.connect.model.WhitelistedSite; +import org.mitre.openid.connect.repository.ApprovedSiteRepository; +import org.mitre.openid.connect.repository.BlacklistedSiteRepository; +import org.mitre.openid.connect.repository.WhitelistedSiteRepository; +import org.mitre.openid.connect.service.MITREidDataService; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.format.annotation.DateTimeFormat.ISO; +import org.springframework.format.datetime.DateFormatter; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.nimbusds.jwt.JWTParser; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; + +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.isA; +import static org.mockito.Matchers.isNull; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +@RunWith(MockitoJUnitRunner.class) +@SuppressWarnings(value = {"rawtypes", "unchecked"}) +public class TestMITREidDataService_1_3 { + + private static Logger logger = LoggerFactory.getLogger(TestMITREidDataService_1_3.class); + + @Mock + private OAuth2ClientRepository clientRepository; + @Mock + private ApprovedSiteRepository approvedSiteRepository; + @Mock + private WhitelistedSiteRepository wlSiteRepository; + @Mock + private BlacklistedSiteRepository blSiteRepository; + @Mock + private AuthenticationHolderRepository authHolderRepository; + @Mock + private OAuth2TokenRepository tokenRepository; + @Mock + private SystemScopeRepository sysScopeRepository; + + @Captor + private ArgumentCaptor capturedRefreshTokens; + @Captor + private ArgumentCaptor capturedAccessTokens; + @Captor + private ArgumentCaptor capturedClients; + @Captor + private ArgumentCaptor capturedBlacklistedSites; + @Captor + private ArgumentCaptor capturedWhitelistedSites; + @Captor + private ArgumentCaptor capturedApprovedSites; + @Captor + private ArgumentCaptor capturedAuthHolders; + @Captor + private ArgumentCaptor capturedScope; + + @InjectMocks + private MITREidDataService_1_3 dataService; + private DateFormatter formatter; + + @Before + public void prepare() { + formatter = new DateFormatter(); + formatter.setIso(ISO.DATE_TIME); + + Mockito.reset(clientRepository, approvedSiteRepository, authHolderRepository, tokenRepository, sysScopeRepository, wlSiteRepository, blSiteRepository); + } + + @Test + public void testExportRefreshTokens() throws IOException, ParseException { + String expiration1 = "2014-09-10T22:49:44.090+0000"; + Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); + + ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); + when(mockedClient1.getClientId()).thenReturn("mocked_client_1"); + + AuthenticationHolderEntity mockedAuthHolder1 = mock(AuthenticationHolderEntity.class); + when(mockedAuthHolder1.getId()).thenReturn(1L); + + OAuth2RefreshTokenEntity token1 = new OAuth2RefreshTokenEntity(); + token1.setId(1L); + token1.setClient(mockedClient1); + token1.setExpiration(expirationDate1); + token1.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.")); + token1.setAuthenticationHolder(mockedAuthHolder1); + + String expiration2 = "2015-01-07T18:31:50.079+0000"; + Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); + + ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); + when(mockedClient2.getClientId()).thenReturn("mocked_client_2"); + + AuthenticationHolderEntity mockedAuthHolder2 = mock(AuthenticationHolderEntity.class); + when(mockedAuthHolder2.getId()).thenReturn(2L); + + OAuth2RefreshTokenEntity token2 = new OAuth2RefreshTokenEntity(); + token2.setId(2L); + token2.setClient(mockedClient2); + token2.setExpiration(expirationDate2); + token2.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJlYmEyYjc3My0xNjAzLTRmNDAtOWQ3MS1hMGIxZDg1OWE2MDAifQ.")); + token2.setAuthenticationHolder(mockedAuthHolder2); + + Set allRefreshTokens = ImmutableSet.of(token1, token2); + + Mockito.when(clientRepository.getAllClients()).thenReturn(new HashSet()); + Mockito.when(approvedSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(wlSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(blSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(authHolderRepository.getAll()).thenReturn(new ArrayList()); + Mockito.when(tokenRepository.getAllAccessTokens()).thenReturn(new HashSet()); + Mockito.when(tokenRepository.getAllRefreshTokens()).thenReturn(allRefreshTokens); + Mockito.when(sysScopeRepository.getAll()).thenReturn(new HashSet()); + + // do the data export + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = new JsonWriter(stringWriter); + writer.beginObject(); + dataService.exportData(writer); + writer.endObject(); + writer.close(); + + // parse the output as a JSON object for testing + JsonElement elem = new JsonParser().parse(stringWriter.toString()); + JsonObject root = elem.getAsJsonObject(); + + // make sure the root is there + assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_3), is(true)); + + JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_3).getAsJsonObject(); + + // make sure all the root elements are there + assertThat(config.has(MITREidDataService.CLIENTS), is(true)); + assertThat(config.has(MITREidDataService.GRANTS), is(true)); + assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); + assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); + assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); + assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); + + // make sure the root elements are all arrays + assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); + + + // check our refresh token list (this test) + JsonArray refreshTokens = config.get(MITREidDataService.REFRESHTOKENS).getAsJsonArray(); + + assertThat(refreshTokens.size(), is(2)); + // check for both of our refresh tokens in turn + Set checked = new HashSet<>(); + for (JsonElement e : refreshTokens) { + assertThat(e.isJsonObject(), is(true)); + JsonObject token = e.getAsJsonObject(); + + OAuth2RefreshTokenEntity compare = null; + if (token.get("id").getAsLong() == token1.getId()) { + compare = token1; + } else if (token.get("id").getAsLong() == token2.getId()) { + compare = token2; + } + + if (compare == null) { + fail("Could not find matching id: " + token.get("id").getAsString()); + } else { + assertThat(token.get("id").getAsLong(), equalTo(compare.getId())); + assertThat(token.get("clientId").getAsString(), equalTo(compare.getClient().getClientId())); + assertThat(token.get("expiration").getAsString(), equalTo(formatter.print(compare.getExpiration(), Locale.ENGLISH))); + assertThat(token.get("value").getAsString(), equalTo(compare.getValue())); + assertThat(token.get("authenticationHolderId").getAsLong(), equalTo(compare.getAuthenticationHolder().getId())); + checked.add(compare); + } + } + // make sure all of our refresh tokens were found + assertThat(checked.containsAll(allRefreshTokens), is(true)); + } + + private class refreshTokenIdComparator implements Comparator { + @Override + public int compare(OAuth2RefreshTokenEntity entity1, OAuth2RefreshTokenEntity entity2) { + return entity1.getId().compareTo(entity2.getId()); + } + } + + + @Test + public void testImportRefreshTokens() throws IOException, ParseException { + String expiration1 = "2014-09-10T22:49:44.090+0000"; + Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); + + ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); + when(mockedClient1.getClientId()).thenReturn("mocked_client_1"); + + AuthenticationHolderEntity mockedAuthHolder1 = mock(AuthenticationHolderEntity.class); + when(mockedAuthHolder1.getId()).thenReturn(1L); + + OAuth2RefreshTokenEntity token1 = new OAuth2RefreshTokenEntity(); + token1.setId(1L); + token1.setClient(mockedClient1); + token1.setExpiration(expirationDate1); + token1.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.")); + token1.setAuthenticationHolder(mockedAuthHolder1); + + String expiration2 = "2015-01-07T18:31:50.079+0000"; + Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); + + ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); + when(mockedClient2.getClientId()).thenReturn("mocked_client_2"); + + AuthenticationHolderEntity mockedAuthHolder2 = mock(AuthenticationHolderEntity.class); + when(mockedAuthHolder2.getId()).thenReturn(2L); + + OAuth2RefreshTokenEntity token2 = new OAuth2RefreshTokenEntity(); + token2.setId(2L); + token2.setClient(mockedClient2); + token2.setExpiration(expirationDate2); + token2.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJlYmEyYjc3My0xNjAzLTRmNDAtOWQ3MS1hMGIxZDg1OWE2MDAifQ.")); + token2.setAuthenticationHolder(mockedAuthHolder2); + + String configJson = "{" + + "\"" + MITREidDataService.SYSTEMSCOPES + "\": [], " + + "\"" + MITREidDataService.ACCESSTOKENS + "\": [], " + + "\"" + MITREidDataService.CLIENTS + "\": [], " + + "\"" + MITREidDataService.GRANTS + "\": [], " + + "\"" + MITREidDataService.WHITELISTEDSITES + "\": [], " + + "\"" + MITREidDataService.BLACKLISTEDSITES + "\": [], " + + "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + + "\"" + MITREidDataService.REFRESHTOKENS + "\": [" + + + "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+0000\"," + + "\"authenticationHolderId\":1,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.\"}," + + "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+0000\"," + + "\"authenticationHolderId\":2,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJlYmEyYjc3My0xNjAzLTRmNDAtOWQ3MS1hMGIxZDg1OWE2MDAifQ.\"}" + + + " ]" + + "}"; + + logger.debug(configJson); + JsonReader reader = new JsonReader(new StringReader(configJson)); + + final Map fakeDb = new HashMap<>(); + when(tokenRepository.saveRefreshToken(isA(OAuth2RefreshTokenEntity.class))).thenAnswer(new Answer() { + Long id = 332L; + @Override + public OAuth2RefreshTokenEntity answer(InvocationOnMock invocation) throws Throwable { + OAuth2RefreshTokenEntity _token = (OAuth2RefreshTokenEntity) invocation.getArguments()[0]; + if(_token.getId() == null) { + _token.setId(id++); + } + fakeDb.put(_token.getId(), _token); + return _token; + } + }); + when(tokenRepository.getRefreshTokenById(anyLong())).thenAnswer(new Answer() { + @Override + public OAuth2RefreshTokenEntity answer(InvocationOnMock invocation) throws Throwable { + Long _id = (Long) invocation.getArguments()[0]; + return fakeDb.get(_id); + } + }); + when(clientRepository.getClientByClientId(anyString())).thenAnswer(new Answer() { + @Override + public ClientDetailsEntity answer(InvocationOnMock invocation) throws Throwable { + String _clientId = (String) invocation.getArguments()[0]; + ClientDetailsEntity _client = mock(ClientDetailsEntity.class); + when(_client.getClientId()).thenReturn(_clientId); + return _client; + } + }); + when(authHolderRepository.getById(isNull(Long.class))).thenAnswer(new Answer() { + Long id = 131L; + @Override + public AuthenticationHolderEntity answer(InvocationOnMock invocation) throws Throwable { + AuthenticationHolderEntity _auth = mock(AuthenticationHolderEntity.class); + when(_auth.getId()).thenReturn(id); + id++; + return _auth; + } + }); + dataService.importData(reader); + //2 times for token, 2 times to update client, 2 times to update authHolder + verify(tokenRepository, times(6)).saveRefreshToken(capturedRefreshTokens.capture()); + + List savedRefreshTokens = new ArrayList(fakeDb.values()); //capturedRefreshTokens.getAllValues(); + Collections.sort(savedRefreshTokens, new refreshTokenIdComparator()); + + assertThat(savedRefreshTokens.size(), is(2)); + + assertThat(savedRefreshTokens.get(0).getClient().getClientId(), equalTo(token1.getClient().getClientId())); + assertThat(savedRefreshTokens.get(0).getExpiration(), equalTo(token1.getExpiration())); + assertThat(savedRefreshTokens.get(0).getValue(), equalTo(token1.getValue())); + + assertThat(savedRefreshTokens.get(1).getClient().getClientId(), equalTo(token2.getClient().getClientId())); + assertThat(savedRefreshTokens.get(1).getExpiration(), equalTo(token2.getExpiration())); + assertThat(savedRefreshTokens.get(1).getValue(), equalTo(token2.getValue())); + } + + @Test + public void testExportAccessTokens() throws IOException, ParseException { + String expiration1 = "2014-09-10T22:49:44.090+0000"; + Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); + + ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); + when(mockedClient1.getClientId()).thenReturn("mocked_client_1"); + + AuthenticationHolderEntity mockedAuthHolder1 = mock(AuthenticationHolderEntity.class); + when(mockedAuthHolder1.getId()).thenReturn(1L); + + OAuth2AccessTokenEntity token1 = new OAuth2AccessTokenEntity(); + token1.setId(1L); + token1.setClient(mockedClient1); + token1.setExpiration(expirationDate1); + token1.setJwt(JWTParser.parse("eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3ODk5NjgsInN1YiI6IjkwMzQyLkFTREZKV0ZBIiwiYXRfaGFzaCI6InptTmt1QmNRSmNYQktNaVpFODZqY0EiLCJhdWQiOlsiY2xpZW50Il0sImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdDo4MDgwXC9vcGVuaWQtY29ubmVjdC1zZXJ2ZXItd2ViYXBwXC8iLCJpYXQiOjE0MTI3ODkzNjh9.xkEJ9IMXpH7qybWXomfq9WOOlpGYnrvGPgey9UQ4GLzbQx7JC0XgJK83PmrmBZosvFPCmota7FzI_BtwoZLgAZfFiH6w3WIlxuogoH-TxmYbxEpTHoTsszZppkq9mNgOlArV4jrR9y3TPo4MovsH71dDhS_ck-CvAlJunHlqhs0")); + token1.setAuthenticationHolder(mockedAuthHolder1); + token1.setScope(ImmutableSet.of("id-token")); + token1.setTokenType("Bearer"); + + String expiration2 = "2015-01-07T18:31:50.079+0000"; + Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); + + ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); + when(mockedClient2.getClientId()).thenReturn("mocked_client_2"); + + AuthenticationHolderEntity mockedAuthHolder2 = mock(AuthenticationHolderEntity.class); + when(mockedAuthHolder2.getId()).thenReturn(2L); + + OAuth2RefreshTokenEntity mockRefreshToken2 = mock(OAuth2RefreshTokenEntity.class); + when(mockRefreshToken2.getId()).thenReturn(1L); + + OAuth2AccessTokenEntity token2 = new OAuth2AccessTokenEntity(); + token2.setId(2L); + token2.setClient(mockedClient2); + token2.setExpiration(expirationDate2); + token2.setJwt(JWTParser.parse("eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3OTI5NjgsImF1ZCI6WyJjbGllbnQiXSwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwODBcL29wZW5pZC1jb25uZWN0LXNlcnZlci13ZWJhcHBcLyIsImp0aSI6IjBmZGE5ZmRiLTYyYzItNGIzZS05OTdiLWU0M2VhMDUwMzNiOSIsImlhdCI6MTQxMjc4OTM2OH0.xgaVpRLYE5MzbgXfE0tZt823tjAm6Oh3_kdR1P2I9jRLR6gnTlBQFlYi3Y_0pWNnZSerbAE8Tn6SJHZ9k-curVG0-ByKichV7CNvgsE5X_2wpEaUzejvKf8eZ-BammRY-ie6yxSkAarcUGMvGGOLbkFcz5CtrBpZhfd75J49BIQ")); + token2.setAuthenticationHolder(mockedAuthHolder2); + token2.setIdToken(token1); + token2.setRefreshToken(mockRefreshToken2); + token2.setScope(ImmutableSet.of("openid", "offline_access", "email", "profile")); + token2.setTokenType("Bearer"); + + Set allAccessTokens = ImmutableSet.of(token1, token2); + + Mockito.when(clientRepository.getAllClients()).thenReturn(new HashSet()); + Mockito.when(approvedSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(wlSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(blSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(authHolderRepository.getAll()).thenReturn(new ArrayList()); + Mockito.when(tokenRepository.getAllRefreshTokens()).thenReturn(new HashSet()); + Mockito.when(tokenRepository.getAllAccessTokens()).thenReturn(allAccessTokens); + Mockito.when(sysScopeRepository.getAll()).thenReturn(new HashSet()); + + // do the data export + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = new JsonWriter(stringWriter); + writer.beginObject(); + dataService.exportData(writer); + writer.endObject(); + writer.close(); + + // parse the output as a JSON object for testing + JsonElement elem = new JsonParser().parse(stringWriter.toString()); + JsonObject root = elem.getAsJsonObject(); + + // make sure the root is there + assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_3), is(true)); + + JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_3).getAsJsonObject(); + + // make sure all the root elements are there + assertThat(config.has(MITREidDataService.CLIENTS), is(true)); + assertThat(config.has(MITREidDataService.GRANTS), is(true)); + assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); + assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); + assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); + assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); + + // make sure the root elements are all arrays + assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); + + + // check our access token list (this test) + JsonArray accessTokens = config.get(MITREidDataService.ACCESSTOKENS).getAsJsonArray(); + + assertThat(accessTokens.size(), is(2)); + // check for both of our access tokens in turn + Set checked = new HashSet<>(); + for (JsonElement e : accessTokens) { + assertTrue(e.isJsonObject()); + JsonObject token = e.getAsJsonObject(); + + OAuth2AccessTokenEntity compare = null; + if (token.get("id").getAsLong() == token1.getId().longValue()) { + compare = token1; + } else if (token.get("id").getAsLong() == token2.getId().longValue()) { + compare = token2; + } + + if (compare == null) { + fail("Could not find matching id: " + token.get("id").getAsString()); + } else { + assertThat(token.get("id").getAsLong(), equalTo(compare.getId())); + assertThat(token.get("clientId").getAsString(), equalTo(compare.getClient().getClientId())); + assertThat(token.get("expiration").getAsString(), equalTo(formatter.print(compare.getExpiration(), Locale.ENGLISH))); + assertThat(token.get("value").getAsString(), equalTo(compare.getValue())); + assertThat(token.get("type").getAsString(), equalTo(compare.getTokenType())); + assertThat(token.get("authenticationHolderId").getAsLong(), equalTo(compare.getAuthenticationHolder().getId())); + assertTrue(token.get("scope").isJsonArray()); + assertThat(jsonArrayToStringSet(token.getAsJsonArray("scope")), equalTo(compare.getScope())); + if(token.get("idTokenId").isJsonNull()) { + assertNull(compare.getIdToken()); + } else { + assertThat(token.get("idTokenId").getAsLong(), equalTo(compare.getIdToken().getId())); + } + if(token.get("refreshTokenId").isJsonNull()) { + assertNull(compare.getIdToken()); + } else { + assertThat(token.get("refreshTokenId").getAsLong(), equalTo(compare.getRefreshToken().getId())); + } + checked.add(compare); + } + } + // make sure all of our access tokens were found + assertThat(checked.containsAll(allAccessTokens), is(true)); + } + + private class accessTokenIdComparator implements Comparator { + @Override + public int compare(OAuth2AccessTokenEntity entity1, OAuth2AccessTokenEntity entity2) { + return entity1.getId().compareTo(entity2.getId()); + } + } + + @Test + public void testImportAccessTokens() throws IOException, ParseException { + String expiration1 = "2014-09-10T22:49:44.090+0000"; + Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); + + ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); + when(mockedClient1.getClientId()).thenReturn("mocked_client_1"); + + AuthenticationHolderEntity mockedAuthHolder1 = mock(AuthenticationHolderEntity.class); + when(mockedAuthHolder1.getId()).thenReturn(1L); + + OAuth2AccessTokenEntity token1 = new OAuth2AccessTokenEntity(); + token1.setId(1L); + token1.setClient(mockedClient1); + token1.setExpiration(expirationDate1); + token1.setJwt(JWTParser.parse("eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3ODk5NjgsInN1YiI6IjkwMzQyLkFTREZKV0ZBIiwiYXRfaGFzaCI6InptTmt1QmNRSmNYQktNaVpFODZqY0EiLCJhdWQiOlsiY2xpZW50Il0sImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdDo4MDgwXC9vcGVuaWQtY29ubmVjdC1zZXJ2ZXItd2ViYXBwXC8iLCJpYXQiOjE0MTI3ODkzNjh9.xkEJ9IMXpH7qybWXomfq9WOOlpGYnrvGPgey9UQ4GLzbQx7JC0XgJK83PmrmBZosvFPCmota7FzI_BtwoZLgAZfFiH6w3WIlxuogoH-TxmYbxEpTHoTsszZppkq9mNgOlArV4jrR9y3TPo4MovsH71dDhS_ck-CvAlJunHlqhs0")); + token1.setAuthenticationHolder(mockedAuthHolder1); + token1.setScope(ImmutableSet.of("id-token")); + token1.setTokenType("Bearer"); + + String expiration2 = "2015-01-07T18:31:50.079+0000"; + Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); + + ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); + when(mockedClient2.getClientId()).thenReturn("mocked_client_2"); + + AuthenticationHolderEntity mockedAuthHolder2 = mock(AuthenticationHolderEntity.class); + when(mockedAuthHolder2.getId()).thenReturn(2L); + + OAuth2RefreshTokenEntity mockRefreshToken2 = mock(OAuth2RefreshTokenEntity.class); + when(mockRefreshToken2.getId()).thenReturn(1L); + + OAuth2AccessTokenEntity token2 = new OAuth2AccessTokenEntity(); + token2.setId(2L); + token2.setClient(mockedClient2); + token2.setExpiration(expirationDate2); + token2.setJwt(JWTParser.parse("eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3OTI5NjgsImF1ZCI6WyJjbGllbnQiXSwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwODBcL29wZW5pZC1jb25uZWN0LXNlcnZlci13ZWJhcHBcLyIsImp0aSI6IjBmZGE5ZmRiLTYyYzItNGIzZS05OTdiLWU0M2VhMDUwMzNiOSIsImlhdCI6MTQxMjc4OTM2OH0.xgaVpRLYE5MzbgXfE0tZt823tjAm6Oh3_kdR1P2I9jRLR6gnTlBQFlYi3Y_0pWNnZSerbAE8Tn6SJHZ9k-curVG0-ByKichV7CNvgsE5X_2wpEaUzejvKf8eZ-BammRY-ie6yxSkAarcUGMvGGOLbkFcz5CtrBpZhfd75J49BIQ")); + token2.setAuthenticationHolder(mockedAuthHolder2); + token2.setIdToken(token1); + token2.setRefreshToken(mockRefreshToken2); + token2.setScope(ImmutableSet.of("openid", "offline_access", "email", "profile")); + token2.setTokenType("Bearer"); + + String configJson = "{" + + "\"" + MITREidDataService.SYSTEMSCOPES + "\": [], " + + "\"" + MITREidDataService.REFRESHTOKENS + "\": [], " + + "\"" + MITREidDataService.CLIENTS + "\": [], " + + "\"" + MITREidDataService.GRANTS + "\": [], " + + "\"" + MITREidDataService.WHITELISTEDSITES + "\": [], " + + "\"" + MITREidDataService.BLACKLISTEDSITES + "\": [], " + + "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + + "\"" + MITREidDataService.ACCESSTOKENS + "\": [" + + + "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+0000\"," + + "\"refreshTokenId\":null,\"idTokenId\":null,\"scope\":[\"id-token\"],\"type\":\"Bearer\"," + + "\"authenticationHolderId\":1,\"value\":\"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3ODk5NjgsInN1YiI6IjkwMzQyLkFTREZKV0ZBIiwiYXRfaGFzaCI6InptTmt1QmNRSmNYQktNaVpFODZqY0EiLCJhdWQiOlsiY2xpZW50Il0sImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdDo4MDgwXC9vcGVuaWQtY29ubmVjdC1zZXJ2ZXItd2ViYXBwXC8iLCJpYXQiOjE0MTI3ODkzNjh9.xkEJ9IMXpH7qybWXomfq9WOOlpGYnrvGPgey9UQ4GLzbQx7JC0XgJK83PmrmBZosvFPCmota7FzI_BtwoZLgAZfFiH6w3WIlxuogoH-TxmYbxEpTHoTsszZppkq9mNgOlArV4jrR9y3TPo4MovsH71dDhS_ck-CvAlJunHlqhs0\"}," + + "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+0000\"," + + "\"refreshTokenId\":1,\"idTokenId\":1,\"scope\":[\"openid\",\"offline_access\",\"email\",\"profile\"],\"type\":\"Bearer\"," + + "\"authenticationHolderId\":2,\"value\":\"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3OTI5NjgsImF1ZCI6WyJjbGllbnQiXSwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwODBcL29wZW5pZC1jb25uZWN0LXNlcnZlci13ZWJhcHBcLyIsImp0aSI6IjBmZGE5ZmRiLTYyYzItNGIzZS05OTdiLWU0M2VhMDUwMzNiOSIsImlhdCI6MTQxMjc4OTM2OH0.xgaVpRLYE5MzbgXfE0tZt823tjAm6Oh3_kdR1P2I9jRLR6gnTlBQFlYi3Y_0pWNnZSerbAE8Tn6SJHZ9k-curVG0-ByKichV7CNvgsE5X_2wpEaUzejvKf8eZ-BammRY-ie6yxSkAarcUGMvGGOLbkFcz5CtrBpZhfd75J49BIQ\"}" + + + " ]" + + "}"; + + + logger.debug(configJson); + + JsonReader reader = new JsonReader(new StringReader(configJson)); + + final Map fakeDb = new HashMap<>(); + when(tokenRepository.saveAccessToken(isA(OAuth2AccessTokenEntity.class))).thenAnswer(new Answer() { + Long id = 324L; + @Override + public OAuth2AccessTokenEntity answer(InvocationOnMock invocation) throws Throwable { + OAuth2AccessTokenEntity _token = (OAuth2AccessTokenEntity) invocation.getArguments()[0]; + if(_token.getId() == null) { + _token.setId(id++); + } + fakeDb.put(_token.getId(), _token); + return _token; + } + }); + when(tokenRepository.getAccessTokenById(anyLong())).thenAnswer(new Answer() { + @Override + public OAuth2AccessTokenEntity answer(InvocationOnMock invocation) throws Throwable { + Long _id = (Long) invocation.getArguments()[0]; + return fakeDb.get(_id); + } + }); + when(clientRepository.getClientByClientId(anyString())).thenAnswer(new Answer() { + @Override + public ClientDetailsEntity answer(InvocationOnMock invocation) throws Throwable { + String _clientId = (String) invocation.getArguments()[0]; + ClientDetailsEntity _client = mock(ClientDetailsEntity.class); + when(_client.getClientId()).thenReturn(_clientId); + return _client; + } + }); + when(authHolderRepository.getById(isNull(Long.class))).thenAnswer(new Answer() { + Long id = 133L; + @Override + public AuthenticationHolderEntity answer(InvocationOnMock invocation) throws Throwable { + AuthenticationHolderEntity _auth = mock(AuthenticationHolderEntity.class); + when(_auth.getId()).thenReturn(id); + id++; + return _auth; + } + }); + dataService.importData(reader); + //2 times for token, 2 times to update client, 2 times to update authHolder, 2 times to update id token, 2 times to update refresh token + verify(tokenRepository, times(8)).saveAccessToken(capturedAccessTokens.capture()); + + List savedAccessTokens = new ArrayList(fakeDb.values()); //capturedAccessTokens.getAllValues(); + Collections.sort(savedAccessTokens, new accessTokenIdComparator()); + + assertThat(savedAccessTokens.size(), is(2)); + + assertThat(savedAccessTokens.get(0).getClient().getClientId(), equalTo(token1.getClient().getClientId())); + assertThat(savedAccessTokens.get(0).getExpiration(), equalTo(token1.getExpiration())); + assertThat(savedAccessTokens.get(0).getValue(), equalTo(token1.getValue())); + + assertThat(savedAccessTokens.get(1).getClient().getClientId(), equalTo(token2.getClient().getClientId())); + assertThat(savedAccessTokens.get(1).getExpiration(), equalTo(token2.getExpiration())); + assertThat(savedAccessTokens.get(1).getValue(), equalTo(token2.getValue())); + } + + @Test + public void testExportClients() throws IOException { + ClientDetailsEntity client1 = new ClientDetailsEntity(); + client1.setId(1L); + client1.setAccessTokenValiditySeconds(3600); + client1.setClientId("client1"); + client1.setClientSecret("clientsecret1"); + client1.setRedirectUris(ImmutableSet.of("http://foo.com/")); + client1.setScope(ImmutableSet.of("foo", "bar", "baz", "dolphin")); + client1.setGrantTypes(ImmutableSet.of("implicit", "authorization_code", "urn:ietf:params:oauth:grant_type:redelegate", "refresh_token")); + client1.setAllowIntrospection(true); + + ClientDetailsEntity client2 = new ClientDetailsEntity(); + client2.setId(2L); + client2.setAccessTokenValiditySeconds(3600); + client2.setClientId("client2"); + client2.setClientSecret("clientsecret2"); + client2.setRedirectUris(ImmutableSet.of("http://bar.baz.com/")); + client2.setScope(ImmutableSet.of("foo", "dolphin", "electric-wombat")); + client2.setGrantTypes(ImmutableSet.of("client_credentials", "urn:ietf:params:oauth:grant_type:redelegate")); + client2.setAllowIntrospection(false); + client2.setCodeChallengeMethod(PKCEAlgorithm.S256); + + Set allClients = ImmutableSet.of(client1, client2); + + Mockito.when(clientRepository.getAllClients()).thenReturn(allClients); + Mockito.when(approvedSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(wlSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(blSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(authHolderRepository.getAll()).thenReturn(new ArrayList()); + Mockito.when(tokenRepository.getAllAccessTokens()).thenReturn(new HashSet()); + Mockito.when(tokenRepository.getAllRefreshTokens()).thenReturn(new HashSet()); + Mockito.when(sysScopeRepository.getAll()).thenReturn(new HashSet()); + + // do the data export + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = new JsonWriter(stringWriter); + writer.beginObject(); + dataService.exportData(writer); + writer.endObject(); + writer.close(); + + // parse the output as a JSON object for testing + JsonElement elem = new JsonParser().parse(stringWriter.toString()); + JsonObject root = elem.getAsJsonObject(); + + // make sure the root is there + assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_3), is(true)); + + JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_3).getAsJsonObject(); + + // make sure all the root elements are there + assertThat(config.has(MITREidDataService.CLIENTS), is(true)); + assertThat(config.has(MITREidDataService.GRANTS), is(true)); + assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); + assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); + assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); + assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); + + // make sure the root elements are all arrays + assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); + + + // check our client list (this test) + JsonArray clients = config.get(MITREidDataService.CLIENTS).getAsJsonArray(); + + assertThat(clients.size(), is(2)); + // check for both of our clients in turn + Set checked = new HashSet<>(); + for (JsonElement e : clients) { + assertThat(e.isJsonObject(), is(true)); + JsonObject client = e.getAsJsonObject(); + + ClientDetailsEntity compare = null; + if (client.get("clientId").getAsString().equals(client1.getClientId())) { + compare = client1; + } else if (client.get("clientId").getAsString().equals(client2.getClientId())) { + compare = client2; + } + + if (compare == null) { + fail("Could not find matching clientId: " + client.get("clientId").getAsString()); + } else { + assertThat(client.get("clientId").getAsString(), equalTo(compare.getClientId())); + assertThat(client.get("secret").getAsString(), equalTo(compare.getClientSecret())); + assertThat(client.get("accessTokenValiditySeconds").getAsInt(), equalTo(compare.getAccessTokenValiditySeconds())); + assertThat(client.get("allowIntrospection").getAsBoolean(), equalTo(compare.isAllowIntrospection())); + assertThat(jsonArrayToStringSet(client.get("redirectUris").getAsJsonArray()), equalTo(compare.getRedirectUris())); + assertThat(jsonArrayToStringSet(client.get("scope").getAsJsonArray()), equalTo(compare.getScope())); + assertThat(jsonArrayToStringSet(client.get("grantTypes").getAsJsonArray()), equalTo(compare.getGrantTypes())); + assertThat((client.has("codeChallengeMethod") && !client.get("codeChallengeMethod").isJsonNull()) ? PKCEAlgorithm.parse(client.get("codeChallengeMethod").getAsString()) : null, equalTo(compare.getCodeChallengeMethod())); + checked.add(compare); + } + } + // make sure all of our clients were found + assertThat(checked.containsAll(allClients), is(true)); + } + + @Test + public void testImportClients() throws IOException { + ClientDetailsEntity client1 = new ClientDetailsEntity(); + client1.setId(1L); + client1.setAccessTokenValiditySeconds(3600); + client1.setClientId("client1"); + client1.setClientSecret("clientsecret1"); + client1.setRedirectUris(ImmutableSet.of("http://foo.com/")); + client1.setScope(ImmutableSet.of("foo", "bar", "baz", "dolphin")); + client1.setGrantTypes(ImmutableSet.of("implicit", "authorization_code", "urn:ietf:params:oauth:grant_type:redelegate", "refresh_token")); + client1.setAllowIntrospection(true); + + ClientDetailsEntity client2 = new ClientDetailsEntity(); + client2.setId(2L); + client2.setAccessTokenValiditySeconds(3600); + client2.setClientId("client2"); + client2.setClientSecret("clientsecret2"); + client2.setRedirectUris(ImmutableSet.of("http://bar.baz.com/")); + client2.setScope(ImmutableSet.of("foo", "dolphin", "electric-wombat")); + client2.setGrantTypes(ImmutableSet.of("client_credentials", "urn:ietf:params:oauth:grant_type:redelegate")); + client2.setAllowIntrospection(false); + + String configJson = "{" + + "\"" + MITREidDataService.SYSTEMSCOPES + "\": [], " + + "\"" + MITREidDataService.ACCESSTOKENS + "\": [], " + + "\"" + MITREidDataService.REFRESHTOKENS + "\": [], " + + "\"" + MITREidDataService.GRANTS + "\": [], " + + "\"" + MITREidDataService.WHITELISTEDSITES + "\": [], " + + "\"" + MITREidDataService.BLACKLISTEDSITES + "\": [], " + + "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + + "\"" + MITREidDataService.CLIENTS + "\": [" + + + "{\"id\":1,\"accessTokenValiditySeconds\":3600,\"clientId\":\"client1\",\"secret\":\"clientsecret1\"," + + "\"redirectUris\":[\"http://foo.com/\"]," + + "\"scope\":[\"foo\",\"bar\",\"baz\",\"dolphin\"]," + + "\"grantTypes\":[\"implicit\",\"authorization_code\",\"urn:ietf:params:oauth:grant_type:redelegate\",\"refresh_token\"]," + + "\"allowIntrospection\":true}," + + "{\"id\":2,\"accessTokenValiditySeconds\":3600,\"clientId\":\"client2\",\"secret\":\"clientsecret2\"," + + "\"redirectUris\":[\"http://bar.baz.com/\"]," + + "\"scope\":[\"foo\",\"dolphin\",\"electric-wombat\"]," + + "\"grantTypes\":[\"client_credentials\",\"urn:ietf:params:oauth:grant_type:redelegate\"]," + + "\"allowIntrospection\":false}" + + + " ]" + + "}"; + + logger.debug(configJson); + + JsonReader reader = new JsonReader(new StringReader(configJson)); + + dataService.importData(reader); + verify(clientRepository, times(2)).saveClient(capturedClients.capture()); + + List savedClients = capturedClients.getAllValues(); + + assertThat(savedClients.size(), is(2)); + + assertThat(savedClients.get(0).getAccessTokenValiditySeconds(), equalTo(client1.getAccessTokenValiditySeconds())); + assertThat(savedClients.get(0).getClientId(), equalTo(client1.getClientId())); + assertThat(savedClients.get(0).getClientSecret(), equalTo(client1.getClientSecret())); + assertThat(savedClients.get(0).getRedirectUris(), equalTo(client1.getRedirectUris())); + assertThat(savedClients.get(0).getScope(), equalTo(client1.getScope())); + assertThat(savedClients.get(0).getGrantTypes(), equalTo(client1.getGrantTypes())); + assertThat(savedClients.get(0).isAllowIntrospection(), equalTo(client1.isAllowIntrospection())); + + assertThat(savedClients.get(1).getAccessTokenValiditySeconds(), equalTo(client2.getAccessTokenValiditySeconds())); + assertThat(savedClients.get(1).getClientId(), equalTo(client2.getClientId())); + assertThat(savedClients.get(1).getClientSecret(), equalTo(client2.getClientSecret())); + assertThat(savedClients.get(1).getRedirectUris(), equalTo(client2.getRedirectUris())); + assertThat(savedClients.get(1).getScope(), equalTo(client2.getScope())); + assertThat(savedClients.get(1).getGrantTypes(), equalTo(client2.getGrantTypes())); + assertThat(savedClients.get(1).isAllowIntrospection(), equalTo(client2.isAllowIntrospection())); + } + + @Test + public void testExportBlacklistedSites() throws IOException { + BlacklistedSite site1 = new BlacklistedSite(); + site1.setId(1L); + site1.setUri("http://foo.com"); + + BlacklistedSite site2 = new BlacklistedSite(); + site2.setId(2L); + site2.setUri("http://bar.com"); + + BlacklistedSite site3 = new BlacklistedSite(); + site3.setId(3L); + site3.setUri("http://baz.com"); + + Set allBlacklistedSites = ImmutableSet.of(site1, site2, site3); + + Mockito.when(clientRepository.getAllClients()).thenReturn(new HashSet()); + Mockito.when(approvedSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(wlSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(blSiteRepository.getAll()).thenReturn(allBlacklistedSites); + Mockito.when(authHolderRepository.getAll()).thenReturn(new ArrayList()); + Mockito.when(tokenRepository.getAllAccessTokens()).thenReturn(new HashSet()); + Mockito.when(tokenRepository.getAllRefreshTokens()).thenReturn(new HashSet()); + Mockito.when(sysScopeRepository.getAll()).thenReturn(new HashSet()); + + // do the data export + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = new JsonWriter(stringWriter); + writer.beginObject(); + dataService.exportData(writer); + writer.endObject(); + writer.close(); + + // parse the output as a JSON object for testing + JsonElement elem = new JsonParser().parse(stringWriter.toString()); + JsonObject root = elem.getAsJsonObject(); + + // make sure the root is there + assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_3), is(true)); + + JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_3).getAsJsonObject(); + + // make sure all the root elements are there + assertThat(config.has(MITREidDataService.CLIENTS), is(true)); + assertThat(config.has(MITREidDataService.GRANTS), is(true)); + assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); + assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); + assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); + assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); + + // make sure the root elements are all arrays + assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); + + // check our scope list (this test) + JsonArray sites = config.get(MITREidDataService.BLACKLISTEDSITES).getAsJsonArray(); + + assertThat(sites.size(), is(3)); + // check for both of our sites in turn + Set checked = new HashSet<>(); + for (JsonElement e : sites) { + assertThat(e.isJsonObject(), is(true)); + JsonObject site = e.getAsJsonObject(); + + BlacklistedSite compare = null; + if (site.get("id").getAsLong() == site1.getId().longValue()) { + compare = site1; + } else if (site.get("id").getAsLong() == site2.getId().longValue()) { + compare = site2; + } else if (site.get("id").getAsLong() == site3.getId().longValue()) { + compare = site3; + } + + if (compare == null) { + fail("Could not find matching blacklisted site id: " + site.get("id").getAsString()); + } else { + assertThat(site.get("uri").getAsString(), equalTo(compare.getUri())); + checked.add(compare); + } + } + // make sure all of our clients were found + assertThat(checked.containsAll(allBlacklistedSites), is(true)); + + } + + @Test + public void testImportBlacklistedSites() throws IOException { + BlacklistedSite site1 = new BlacklistedSite(); + site1.setId(1L); + site1.setUri("http://foo.com"); + + BlacklistedSite site2 = new BlacklistedSite(); + site2.setId(2L); + site2.setUri("http://bar.com"); + + BlacklistedSite site3 = new BlacklistedSite(); + site3.setId(3L); + site3.setUri("http://baz.com"); + + String configJson = "{" + + "\"" + MITREidDataService.CLIENTS + "\": [], " + + "\"" + MITREidDataService.ACCESSTOKENS + "\": [], " + + "\"" + MITREidDataService.REFRESHTOKENS + "\": [], " + + "\"" + MITREidDataService.GRANTS + "\": [], " + + "\"" + MITREidDataService.WHITELISTEDSITES + "\": [], " + + "\"" + MITREidDataService.SYSTEMSCOPES + "\": [], " + + "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + + "\"" + MITREidDataService.BLACKLISTEDSITES + "\": [" + + + "{\"id\":1,\"uri\":\"http://foo.com\"}," + + "{\"id\":2,\"uri\":\"http://bar.com\"}," + + "{\"id\":3,\"uri\":\"http://baz.com\"}" + + + " ]" + + "}"; + + + logger.debug(configJson); + + JsonReader reader = new JsonReader(new StringReader(configJson)); + + dataService.importData(reader); + verify(blSiteRepository, times(3)).save(capturedBlacklistedSites.capture()); + + List savedSites = capturedBlacklistedSites.getAllValues(); + + assertThat(savedSites.size(), is(3)); + + assertThat(savedSites.get(0).getUri(), equalTo(site1.getUri())); + assertThat(savedSites.get(1).getUri(), equalTo(site2.getUri())); + assertThat(savedSites.get(2).getUri(), equalTo(site3.getUri())); + } + + @Test + public void testExportWhitelistedSites() throws IOException { + WhitelistedSite site1 = new WhitelistedSite(); + site1.setId(1L); + site1.setClientId("foo"); + + WhitelistedSite site2 = new WhitelistedSite(); + site2.setId(2L); + site2.setClientId("bar"); + + WhitelistedSite site3 = new WhitelistedSite(); + site3.setId(3L); + site3.setClientId("baz"); + + Set allWhitelistedSites = ImmutableSet.of(site1, site2, site3); + + Mockito.when(clientRepository.getAllClients()).thenReturn(new HashSet()); + Mockito.when(approvedSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(blSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(wlSiteRepository.getAll()).thenReturn(allWhitelistedSites); + Mockito.when(authHolderRepository.getAll()).thenReturn(new ArrayList()); + Mockito.when(tokenRepository.getAllAccessTokens()).thenReturn(new HashSet()); + Mockito.when(tokenRepository.getAllRefreshTokens()).thenReturn(new HashSet()); + Mockito.when(sysScopeRepository.getAll()).thenReturn(new HashSet()); + + // do the data export + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = new JsonWriter(stringWriter); + writer.beginObject(); + dataService.exportData(writer); + writer.endObject(); + writer.close(); + + // parse the output as a JSON object for testing + JsonElement elem = new JsonParser().parse(stringWriter.toString()); + JsonObject root = elem.getAsJsonObject(); + + // make sure the root is there + assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_3), is(true)); + + JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_3).getAsJsonObject(); + + // make sure all the root elements are there + assertThat(config.has(MITREidDataService.CLIENTS), is(true)); + assertThat(config.has(MITREidDataService.GRANTS), is(true)); + assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); + assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); + assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); + assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); + + // make sure the root elements are all arrays + assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); + + // check our scope list (this test) + JsonArray sites = config.get(MITREidDataService.WHITELISTEDSITES).getAsJsonArray(); + + assertThat(sites.size(), is(3)); + // check for both of our sites in turn + Set checked = new HashSet<>(); + for (JsonElement e : sites) { + assertThat(e.isJsonObject(), is(true)); + JsonObject site = e.getAsJsonObject(); + + WhitelistedSite compare = null; + if (site.get("id").getAsLong() == site1.getId().longValue()) { + compare = site1; + } else if (site.get("id").getAsLong() == site2.getId().longValue()) { + compare = site2; + } else if (site.get("id").getAsLong() == site3.getId().longValue()) { + compare = site3; + } + + if (compare == null) { + fail("Could not find matching whitelisted site id: " + site.get("id").getAsString()); + } else { + assertThat(site.get("clientId").getAsString(), equalTo(compare.getClientId())); + checked.add(compare); + } + } + // make sure all of our clients were found + assertThat(checked.containsAll(allWhitelistedSites), is(true)); + + } + + @Test + public void testImportWhitelistedSites() throws IOException { + WhitelistedSite site1 = new WhitelistedSite(); + site1.setId(1L); + site1.setClientId("foo"); + + WhitelistedSite site2 = new WhitelistedSite(); + site2.setId(2L); + site2.setClientId("bar"); + + WhitelistedSite site3 = new WhitelistedSite(); + site3.setId(3L); + site3.setClientId("baz"); + //site3.setAllowedScopes(null); + + String configJson = "{" + + "\"" + MITREidDataService.CLIENTS + "\": [], " + + "\"" + MITREidDataService.ACCESSTOKENS + "\": [], " + + "\"" + MITREidDataService.REFRESHTOKENS + "\": [], " + + "\"" + MITREidDataService.GRANTS + "\": [], " + + "\"" + MITREidDataService.BLACKLISTEDSITES + "\": [], " + + "\"" + MITREidDataService.SYSTEMSCOPES + "\": [], " + + "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + + "\"" + MITREidDataService.WHITELISTEDSITES + "\": [" + + + "{\"id\":1,\"clientId\":\"foo\"}," + + "{\"id\":2,\"clientId\":\"bar\"}," + + "{\"id\":3,\"clientId\":\"baz\"}" + + + " ]" + + "}"; + + logger.debug(configJson); + + JsonReader reader = new JsonReader(new StringReader(configJson)); + + final Map fakeDb = new HashMap<>(); + when(wlSiteRepository.save(isA(WhitelistedSite.class))).thenAnswer(new Answer() { + Long id = 333L; + @Override + public WhitelistedSite answer(InvocationOnMock invocation) throws Throwable { + WhitelistedSite _site = (WhitelistedSite) invocation.getArguments()[0]; + if(_site.getId() == null) { + _site.setId(id++); + } + fakeDb.put(_site.getId(), _site); + return _site; + } + }); + when(wlSiteRepository.getById(anyLong())).thenAnswer(new Answer() { + @Override + public WhitelistedSite answer(InvocationOnMock invocation) throws Throwable { + Long _id = (Long) invocation.getArguments()[0]; + return fakeDb.get(_id); + } + }); + + dataService.importData(reader); + verify(wlSiteRepository, times(3)).save(capturedWhitelistedSites.capture()); + + List savedSites = capturedWhitelistedSites.getAllValues(); + + assertThat(savedSites.size(), is(3)); + + assertThat(savedSites.get(0).getClientId(), equalTo(site1.getClientId())); + assertThat(savedSites.get(1).getClientId(), equalTo(site2.getClientId())); + assertThat(savedSites.get(2).getClientId(), equalTo(site3.getClientId())); + } + + @Test + public void testExportGrants() throws IOException, ParseException { + Date creationDate1 = formatter.parse("2014-09-10T22:49:44.090+0000", Locale.ENGLISH); + Date accessDate1 = formatter.parse("2014-09-10T23:49:44.090+0000", Locale.ENGLISH); + + OAuth2AccessTokenEntity mockToken1 = mock(OAuth2AccessTokenEntity.class); + when(mockToken1.getId()).thenReturn(1L); + + ApprovedSite site1 = new ApprovedSite(); + site1.setId(1L); + site1.setClientId("foo"); + site1.setCreationDate(creationDate1); + site1.setAccessDate(accessDate1); + site1.setUserId("user1"); + site1.setAllowedScopes(ImmutableSet.of("openid", "phone")); + site1.setApprovedAccessTokens(ImmutableSet.of(mockToken1)); + + Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+0000", Locale.ENGLISH); + Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+0000", Locale.ENGLISH); + Date timeoutDate2 = formatter.parse("2014-10-01T20:49:44.090+0000", Locale.ENGLISH); + + ApprovedSite site2 = new ApprovedSite(); + site2.setId(2L); + site2.setClientId("bar"); + site2.setCreationDate(creationDate2); + site2.setAccessDate(accessDate2); + site2.setUserId("user2"); + site2.setAllowedScopes(ImmutableSet.of("openid", "offline_access", "email", "profile")); + site2.setTimeoutDate(timeoutDate2); + + Set allApprovedSites = ImmutableSet.of(site1, site2); + + Mockito.when(clientRepository.getAllClients()).thenReturn(new HashSet()); + Mockito.when(approvedSiteRepository.getAll()).thenReturn(allApprovedSites); + Mockito.when(blSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(wlSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(authHolderRepository.getAll()).thenReturn(new ArrayList()); + Mockito.when(tokenRepository.getAllAccessTokens()).thenReturn(new HashSet()); + Mockito.when(tokenRepository.getAllRefreshTokens()).thenReturn(new HashSet()); + Mockito.when(sysScopeRepository.getAll()).thenReturn(new HashSet()); + + // do the data export + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = new JsonWriter(stringWriter); + writer.beginObject(); + dataService.exportData(writer); + writer.endObject(); + writer.close(); + + // parse the output as a JSON object for testing + JsonElement elem = new JsonParser().parse(stringWriter.toString()); + JsonObject root = elem.getAsJsonObject(); + + // make sure the root is there + assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_3), is(true)); + + JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_3).getAsJsonObject(); + + // make sure all the root elements are there + assertThat(config.has(MITREidDataService.CLIENTS), is(true)); + assertThat(config.has(MITREidDataService.GRANTS), is(true)); + assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); + assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); + assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); + assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); + + // make sure the root elements are all arrays + assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); + + // check our scope list (this test) + JsonArray sites = config.get(MITREidDataService.GRANTS).getAsJsonArray(); + + assertThat(sites.size(), is(2)); + // check for both of our sites in turn + Set checked = new HashSet<>(); + for (JsonElement e : sites) { + assertThat(e.isJsonObject(), is(true)); + JsonObject site = e.getAsJsonObject(); + + ApprovedSite compare = null; + if (site.get("id").getAsLong() == site1.getId().longValue()) { + compare = site1; + } else if (site.get("id").getAsLong() == site2.getId().longValue()) { + compare = site2; + } + + if (compare == null) { + fail("Could not find matching whitelisted site id: " + site.get("id").getAsString()); + } else { + assertThat(site.get("clientId").getAsString(), equalTo(compare.getClientId())); + assertThat(site.get("creationDate").getAsString(), equalTo(formatter.print(compare.getCreationDate(), Locale.ENGLISH))); + assertThat(site.get("accessDate").getAsString(), equalTo(formatter.print(compare.getAccessDate(), Locale.ENGLISH))); + if(site.get("timeoutDate").isJsonNull()) { + assertNull(compare.getTimeoutDate()); + } else { + assertThat(site.get("timeoutDate").getAsString(), equalTo(formatter.print(compare.getTimeoutDate(), Locale.ENGLISH))); + } + assertThat(site.get("userId").getAsString(), equalTo(compare.getUserId())); + assertThat(jsonArrayToStringSet(site.getAsJsonArray("allowedScopes")), equalTo(compare.getAllowedScopes())); + if (site.get("approvedAccessTokens").isJsonNull() || site.getAsJsonArray("approvedAccessTokens") == null) { + assertTrue(compare.getApprovedAccessTokens() == null || compare.getApprovedAccessTokens().isEmpty()); + } else { + assertNotNull(compare.getApprovedAccessTokens()); + Set tokenIds = new HashSet<>(); + for(OAuth2AccessTokenEntity entity : compare.getApprovedAccessTokens()) { + tokenIds.add(entity.getId().toString()); + } + assertThat(jsonArrayToStringSet(site.getAsJsonArray("approvedAccessTokens")), equalTo(tokenIds)); + } + checked.add(compare); + } + } + // make sure all of our clients were found + assertThat(checked.containsAll(allApprovedSites), is(true)); + } + + @Test + public void testImportGrants() throws IOException, ParseException { + Date creationDate1 = formatter.parse("2014-09-10T22:49:44.090+0000", Locale.ENGLISH); + Date accessDate1 = formatter.parse("2014-09-10T23:49:44.090+0000", Locale.ENGLISH); + + OAuth2AccessTokenEntity mockToken1 = mock(OAuth2AccessTokenEntity.class); + when(mockToken1.getId()).thenReturn(1L); + + ApprovedSite site1 = new ApprovedSite(); + site1.setId(1L); + site1.setClientId("foo"); + site1.setCreationDate(creationDate1); + site1.setAccessDate(accessDate1); + site1.setUserId("user1"); + site1.setAllowedScopes(ImmutableSet.of("openid", "phone")); + site1.setApprovedAccessTokens(ImmutableSet.of(mockToken1)); + + Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+0000", Locale.ENGLISH); + Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+0000", Locale.ENGLISH); + Date timeoutDate2 = formatter.parse("2014-10-01T20:49:44.090+0000", Locale.ENGLISH); + + ApprovedSite site2 = new ApprovedSite(); + site2.setId(2L); + site2.setClientId("bar"); + site2.setCreationDate(creationDate2); + site2.setAccessDate(accessDate2); + site2.setUserId("user2"); + site2.setAllowedScopes(ImmutableSet.of("openid", "offline_access", "email", "profile")); + site2.setTimeoutDate(timeoutDate2); + + String configJson = "{" + + "\"" + MITREidDataService.CLIENTS + "\": [], " + + "\"" + MITREidDataService.ACCESSTOKENS + "\": [], " + + "\"" + MITREidDataService.REFRESHTOKENS + "\": [], " + + "\"" + MITREidDataService.WHITELISTEDSITES + "\": [], " + + "\"" + MITREidDataService.BLACKLISTEDSITES + "\": [], " + + "\"" + MITREidDataService.SYSTEMSCOPES + "\": [], " + + "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + + "\"" + MITREidDataService.GRANTS + "\": [" + + + "{\"id\":1,\"clientId\":\"foo\",\"creationDate\":\"2014-09-10T22:49:44.090+0000\",\"accessDate\":\"2014-09-10T23:49:44.090+0000\"," + + "\"userId\":\"user1\",\"whitelistedSiteId\":null,\"allowedScopes\":[\"openid\",\"phone\"], \"whitelistedSiteId\":1," + + "\"approvedAccessTokens\":[1]}," + + "{\"id\":2,\"clientId\":\"bar\",\"creationDate\":\"2014-09-11T18:49:44.090+0000\",\"accessDate\":\"2014-09-11T20:49:44.090+0000\"," + + "\"timeoutDate\":\"2014-10-01T20:49:44.090+0000\",\"userId\":\"user2\"," + + "\"allowedScopes\":[\"openid\",\"offline_access\",\"email\",\"profile\"]}" + + + " ]" + + "}"; + + logger.debug(configJson); + + JsonReader reader = new JsonReader(new StringReader(configJson)); + + final Map fakeDb = new HashMap<>(); + when(approvedSiteRepository.save(isA(ApprovedSite.class))).thenAnswer(new Answer() { + Long id = 364L; + @Override + public ApprovedSite answer(InvocationOnMock invocation) throws Throwable { + ApprovedSite _site = (ApprovedSite) invocation.getArguments()[0]; + if(_site.getId() == null) { + _site.setId(id++); + } + fakeDb.put(_site.getId(), _site); + return _site; + } + }); + when(approvedSiteRepository.getById(anyLong())).thenAnswer(new Answer() { + @Override + public ApprovedSite answer(InvocationOnMock invocation) throws Throwable { + Long _id = (Long) invocation.getArguments()[0]; + return fakeDb.get(_id); + } + }); + when(wlSiteRepository.getById(isNull(Long.class))).thenAnswer(new Answer() { + Long id = 432L; + @Override + public WhitelistedSite answer(InvocationOnMock invocation) throws Throwable { + WhitelistedSite _site = mock(WhitelistedSite.class); + when(_site.getId()).thenReturn(id++); + return _site; + } + }); + when(tokenRepository.getAccessTokenById(isNull(Long.class))).thenAnswer(new Answer() { + Long id = 245L; + @Override + public OAuth2AccessTokenEntity answer(InvocationOnMock invocation) throws Throwable { + OAuth2AccessTokenEntity _token = mock(OAuth2AccessTokenEntity.class); + when(_token.getId()).thenReturn(id++); + return _token; + } + }); + + dataService.importData(reader); + //2 for sites, 1 for updating access token ref on #1 + verify(approvedSiteRepository, times(3)).save(capturedApprovedSites.capture()); + + List savedSites = new ArrayList(fakeDb.values()); + + assertThat(savedSites.size(), is(2)); + + assertThat(savedSites.get(0).getClientId(), equalTo(site1.getClientId())); + assertThat(savedSites.get(0).getAccessDate(), equalTo(site1.getAccessDate())); + assertThat(savedSites.get(0).getCreationDate(), equalTo(site1.getCreationDate())); + assertThat(savedSites.get(0).getAllowedScopes(), equalTo(site1.getAllowedScopes())); + assertThat(savedSites.get(0).getTimeoutDate(), equalTo(site1.getTimeoutDate())); + assertThat(savedSites.get(0).getApprovedAccessTokens().size(), equalTo(site1.getApprovedAccessTokens().size())); + + assertThat(savedSites.get(1).getClientId(), equalTo(site2.getClientId())); + assertThat(savedSites.get(1).getAccessDate(), equalTo(site2.getAccessDate())); + assertThat(savedSites.get(1).getCreationDate(), equalTo(site2.getCreationDate())); + assertThat(savedSites.get(1).getAllowedScopes(), equalTo(site2.getAllowedScopes())); + assertThat(savedSites.get(1).getTimeoutDate(), equalTo(site2.getTimeoutDate())); + assertThat(savedSites.get(1).getApprovedAccessTokens().size(), equalTo(site2.getApprovedAccessTokens().size())); + } + + @Test + public void testExportAuthenticationHolders() throws IOException { + OAuth2Request req1 = new OAuth2Request(new HashMap(), "client1", new ArrayList(), + true, new HashSet(), new HashSet(), "http://foo.com", + new HashSet(), null); + Authentication mockAuth1 = new UsernamePasswordAuthenticationToken("user1", "pass1", AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); + OAuth2Authentication auth1 = new OAuth2Authentication(req1, mockAuth1); + + AuthenticationHolderEntity holder1 = new AuthenticationHolderEntity(); + holder1.setId(1L); + holder1.setAuthentication(auth1); + + OAuth2Request req2 = new OAuth2Request(new HashMap(), "client2", new ArrayList(), + true, new HashSet(), new HashSet(), "http://bar.com", + new HashSet(), null); + OAuth2Authentication auth2 = new OAuth2Authentication(req2, null); + + AuthenticationHolderEntity holder2 = new AuthenticationHolderEntity(); + holder2.setId(2L); + holder2.setAuthentication(auth2); + + List allAuthHolders = ImmutableList.of(holder1, holder2); + + when(clientRepository.getAllClients()).thenReturn(new HashSet()); + when(approvedSiteRepository.getAll()).thenReturn(new HashSet()); + when(wlSiteRepository.getAll()).thenReturn(new HashSet()); + when(blSiteRepository.getAll()).thenReturn(new HashSet()); + when(authHolderRepository.getAll()).thenReturn(allAuthHolders); + when(tokenRepository.getAllAccessTokens()).thenReturn(new HashSet()); + when(tokenRepository.getAllRefreshTokens()).thenReturn(new HashSet()); + when(sysScopeRepository.getAll()).thenReturn(new HashSet()); + + // do the data export + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = new JsonWriter(stringWriter); + writer.beginObject(); + dataService.exportData(writer); + writer.endObject(); + writer.close(); + + // parse the output as a JSON object for testing + JsonElement elem = new JsonParser().parse(stringWriter.toString()); + JsonObject root = elem.getAsJsonObject(); + + // make sure the root is there + assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_3), is(true)); + + JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_3).getAsJsonObject(); + + // make sure all the root elements are there + assertThat(config.has(MITREidDataService.CLIENTS), is(true)); + assertThat(config.has(MITREidDataService.GRANTS), is(true)); + assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); + assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); + assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); + assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); + + // make sure the root elements are all arrays + assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); + + + // check our holder list (this test) + JsonArray holders = config.get(MITREidDataService.AUTHENTICATIONHOLDERS).getAsJsonArray(); + + assertThat(holders.size(), is(2)); + // check for both of our clients in turn + Set checked = new HashSet<>(); + for (JsonElement e : holders) { + assertThat(e.isJsonObject(), is(true)); + JsonObject holder = e.getAsJsonObject(); + + AuthenticationHolderEntity compare = null; + if (holder.get("id").getAsLong() == holder1.getId()) { + compare = holder1; + } else if (holder.get("id").getAsLong() == holder2.getId()) { + compare = holder2; + } + + if (compare == null) { + fail("Could not find matching authentication holder id: " + holder.get("id").getAsString()); + } else { + assertTrue(holder.get("clientId").getAsString().equals(compare.getClientId())); + assertTrue(holder.get("approved").getAsBoolean() == compare.isApproved()); + assertTrue(holder.get("redirectUri").getAsString().equals(compare.getRedirectUri())); + if (compare.getUserAuth() != null) { + assertTrue(holder.get("savedUserAuthentication").isJsonObject()); + JsonObject savedAuth = holder.get("savedUserAuthentication").getAsJsonObject(); + assertTrue(savedAuth.get("name").getAsString().equals(compare.getUserAuth().getName())); + assertTrue(savedAuth.get("authenticated").getAsBoolean() == compare.getUserAuth().isAuthenticated()); + assertTrue(savedAuth.get("sourceClass").getAsString().equals(compare.getUserAuth().getSourceClass())); + } + checked.add(compare); + } + } + // make sure all of our clients were found + assertThat(checked.containsAll(allAuthHolders), is(true)); + } + + @Test + public void testImportAuthenticationHolders() throws IOException { + OAuth2Request req1 = new OAuth2Request(new HashMap(), "client1", new ArrayList(), + true, new HashSet(), new HashSet(), "http://foo.com", + new HashSet(), null); + Authentication mockAuth1 = mock(Authentication.class, withSettings().serializable()); + OAuth2Authentication auth1 = new OAuth2Authentication(req1, mockAuth1); + + AuthenticationHolderEntity holder1 = new AuthenticationHolderEntity(); + holder1.setId(1L); + holder1.setAuthentication(auth1); + + OAuth2Request req2 = new OAuth2Request(new HashMap(), "client2", new ArrayList(), + true, new HashSet(), new HashSet(), "http://bar.com", + new HashSet(), null); + Authentication mockAuth2 = mock(Authentication.class, withSettings().serializable()); + OAuth2Authentication auth2 = new OAuth2Authentication(req2, mockAuth2); + + AuthenticationHolderEntity holder2 = new AuthenticationHolderEntity(); + holder2.setId(2L); + holder2.setAuthentication(auth2); + + String configJson = "{" + + "\"" + MITREidDataService.CLIENTS + "\": [], " + + "\"" + MITREidDataService.ACCESSTOKENS + "\": [], " + + "\"" + MITREidDataService.REFRESHTOKENS + "\": [], " + + "\"" + MITREidDataService.GRANTS + "\": [], " + + "\"" + MITREidDataService.WHITELISTEDSITES + "\": [], " + + "\"" + MITREidDataService.BLACKLISTEDSITES + "\": [], " + + "\"" + MITREidDataService.SYSTEMSCOPES + "\": [], " + + "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [" + + + "{\"id\":1,\"clientId\":\"client1\",\"redirectUri\":\"http://foo.com\"," + + "\"savedUserAuthentication\":null}," + + "{\"id\":2,\"clientId\":\"client2\",\"redirectUri\":\"http://bar.com\"," + + "\"savedUserAuthentication\":null}" + + " ]" + + "}"; + + logger.debug(configJson); + + JsonReader reader = new JsonReader(new StringReader(configJson)); + + final Map fakeDb = new HashMap<>(); + when(authHolderRepository.save(isA(AuthenticationHolderEntity.class))).thenAnswer(new Answer() { + Long id = 243L; + @Override + public AuthenticationHolderEntity answer(InvocationOnMock invocation) throws Throwable { + AuthenticationHolderEntity _site = (AuthenticationHolderEntity) invocation.getArguments()[0]; + if(_site.getId() == null) { + _site.setId(id++); + } + fakeDb.put(_site.getId(), _site); + return _site; + } + }); + + dataService.importData(reader); + verify(authHolderRepository, times(2)).save(capturedAuthHolders.capture()); + + List savedAuthHolders = capturedAuthHolders.getAllValues(); + + assertThat(savedAuthHolders.size(), is(2)); + assertThat(savedAuthHolders.get(0).getAuthentication().getOAuth2Request().getClientId(), equalTo(holder1.getAuthentication().getOAuth2Request().getClientId())); + assertThat(savedAuthHolders.get(1).getAuthentication().getOAuth2Request().getClientId(), equalTo(holder2.getAuthentication().getOAuth2Request().getClientId())); + } + + @Test + public void testExportSystemScopes() throws IOException { + SystemScope scope1 = new SystemScope(); + scope1.setId(1L); + scope1.setValue("scope1"); + scope1.setDescription("Scope 1"); + scope1.setRestricted(true); + scope1.setDefaultScope(false); + scope1.setIcon("glass"); + + SystemScope scope2 = new SystemScope(); + scope2.setId(2L); + scope2.setValue("scope2"); + scope2.setDescription("Scope 2"); + scope2.setRestricted(false); + scope2.setDefaultScope(false); + scope2.setIcon("ball"); + + SystemScope scope3 = new SystemScope(); + scope3.setId(3L); + scope3.setValue("scope3"); + scope3.setDescription("Scope 3"); + scope3.setRestricted(false); + scope3.setDefaultScope(true); + scope3.setIcon("road"); + + Set allScopes = ImmutableSet.of(scope1, scope2, scope3); + + Mockito.when(clientRepository.getAllClients()).thenReturn(new HashSet()); + Mockito.when(approvedSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(wlSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(blSiteRepository.getAll()).thenReturn(new HashSet()); + Mockito.when(authHolderRepository.getAll()).thenReturn(new ArrayList()); + Mockito.when(tokenRepository.getAllAccessTokens()).thenReturn(new HashSet()); + Mockito.when(tokenRepository.getAllRefreshTokens()).thenReturn(new HashSet()); + Mockito.when(sysScopeRepository.getAll()).thenReturn(allScopes); + + // do the data export + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = new JsonWriter(stringWriter); + writer.beginObject(); + dataService.exportData(writer); + writer.endObject(); + writer.close(); + + // parse the output as a JSON object for testing + JsonElement elem = new JsonParser().parse(stringWriter.toString()); + JsonObject root = elem.getAsJsonObject(); + + // make sure the root is there + assertThat(root.has(MITREidDataService.MITREID_CONNECT_1_3), is(true)); + + JsonObject config = root.get(MITREidDataService.MITREID_CONNECT_1_3).getAsJsonObject(); + + // make sure all the root elements are there + assertThat(config.has(MITREidDataService.CLIENTS), is(true)); + assertThat(config.has(MITREidDataService.GRANTS), is(true)); + assertThat(config.has(MITREidDataService.WHITELISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.BLACKLISTEDSITES), is(true)); + assertThat(config.has(MITREidDataService.REFRESHTOKENS), is(true)); + assertThat(config.has(MITREidDataService.ACCESSTOKENS), is(true)); + assertThat(config.has(MITREidDataService.SYSTEMSCOPES), is(true)); + assertThat(config.has(MITREidDataService.AUTHENTICATIONHOLDERS), is(true)); + + // make sure the root elements are all arrays + assertThat(config.get(MITREidDataService.CLIENTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.GRANTS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.WHITELISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.BLACKLISTEDSITES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.REFRESHTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.ACCESSTOKENS).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.SYSTEMSCOPES).isJsonArray(), is(true)); + assertThat(config.get(MITREidDataService.AUTHENTICATIONHOLDERS).isJsonArray(), is(true)); + + + // check our scope list (this test) + JsonArray scopes = config.get(MITREidDataService.SYSTEMSCOPES).getAsJsonArray(); + + assertThat(scopes.size(), is(3)); + // check for both of our clients in turn + Set checked = new HashSet<>(); + for (JsonElement e : scopes) { + assertThat(e.isJsonObject(), is(true)); + JsonObject scope = e.getAsJsonObject(); + + SystemScope compare = null; + if (scope.get("value").getAsString().equals(scope1.getValue())) { + compare = scope1; + } else if (scope.get("value").getAsString().equals(scope2.getValue())) { + compare = scope2; + } else if (scope.get("value").getAsString().equals(scope3.getValue())) { + compare = scope3; + } + + if (compare == null) { + fail("Could not find matching scope value: " + scope.get("value").getAsString()); + } else { + assertThat(scope.get("value").getAsString(), equalTo(compare.getValue())); + assertThat(scope.get("description").getAsString(), equalTo(compare.getDescription())); + assertThat(scope.get("icon").getAsString(), equalTo(compare.getIcon())); + assertThat(scope.get("restricted").getAsBoolean(), equalTo(compare.isRestricted())); + assertThat(scope.get("defaultScope").getAsBoolean(), equalTo(compare.isDefaultScope())); + checked.add(compare); + } + } + // make sure all of our clients were found + assertThat(checked.containsAll(allScopes), is(true)); + + } + + @Test + public void testImportSystemScopes() throws IOException { + SystemScope scope1 = new SystemScope(); + scope1.setId(1L); + scope1.setValue("scope1"); + scope1.setDescription("Scope 1"); + scope1.setRestricted(true); + scope1.setDefaultScope(false); + scope1.setIcon("glass"); + + SystemScope scope2 = new SystemScope(); + scope2.setId(2L); + scope2.setValue("scope2"); + scope2.setDescription("Scope 2"); + scope2.setRestricted(false); + scope2.setDefaultScope(false); + scope2.setIcon("ball"); + + SystemScope scope3 = new SystemScope(); + scope3.setId(3L); + scope3.setValue("scope3"); + scope3.setDescription("Scope 3"); + scope3.setRestricted(false); + scope3.setDefaultScope(true); + scope3.setIcon("road"); + scope3.setStructured(true); + scope3.setStructuredParamDescription("Structured Parameter"); + + String configJson = "{" + + "\"" + MITREidDataService.CLIENTS + "\": [], " + + "\"" + MITREidDataService.ACCESSTOKENS + "\": [], " + + "\"" + MITREidDataService.REFRESHTOKENS + "\": [], " + + "\"" + MITREidDataService.GRANTS + "\": [], " + + "\"" + MITREidDataService.WHITELISTEDSITES + "\": [], " + + "\"" + MITREidDataService.BLACKLISTEDSITES + "\": [], " + + "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + + "\"" + MITREidDataService.SYSTEMSCOPES + "\": [" + + + "{\"id\":1,\"description\":\"Scope 1\",\"icon\":\"glass\",\"value\":\"scope1\",\"restricted\":true,\"defaultScope\":false}," + + "{\"id\":2,\"description\":\"Scope 2\",\"icon\":\"ball\",\"value\":\"scope2\",\"restricted\":false,\"defaultScope\":false}," + + "{\"id\":3,\"description\":\"Scope 3\",\"icon\":\"road\",\"value\":\"scope3\",\"restricted\":false,\"defaultScope\":true,\"structured\":true,\"structuredParameter\":\"Structured Parameter\"}" + + + " ]" + + "}"; + + logger.debug(configJson); + + JsonReader reader = new JsonReader(new StringReader(configJson)); + + dataService.importData(reader); + verify(sysScopeRepository, times(3)).save(capturedScope.capture()); + + List savedScopes = capturedScope.getAllValues(); + + assertThat(savedScopes.size(), is(3)); + assertThat(savedScopes.get(0).getValue(), equalTo(scope1.getValue())); + assertThat(savedScopes.get(0).getDescription(), equalTo(scope1.getDescription())); + assertThat(savedScopes.get(0).getIcon(), equalTo(scope1.getIcon())); + assertThat(savedScopes.get(0).isDefaultScope(), equalTo(scope1.isDefaultScope())); + assertThat(savedScopes.get(0).isRestricted(), equalTo(scope1.isRestricted())); + assertThat(savedScopes.get(0).isStructured(), equalTo(scope1.isStructured())); + assertThat(savedScopes.get(0).getStructuredParamDescription(), equalTo(scope1.getStructuredParamDescription())); + + assertThat(savedScopes.get(1).getValue(), equalTo(scope2.getValue())); + assertThat(savedScopes.get(1).getDescription(), equalTo(scope2.getDescription())); + assertThat(savedScopes.get(1).getIcon(), equalTo(scope2.getIcon())); + assertThat(savedScopes.get(1).isDefaultScope(), equalTo(scope2.isDefaultScope())); + assertThat(savedScopes.get(1).isRestricted(), equalTo(scope2.isRestricted())); + assertThat(savedScopes.get(1).isStructured(), equalTo(scope2.isStructured())); + assertThat(savedScopes.get(1).getStructuredParamDescription(), equalTo(scope2.getStructuredParamDescription())); + + assertThat(savedScopes.get(2).getValue(), equalTo(scope3.getValue())); + assertThat(savedScopes.get(2).getDescription(), equalTo(scope3.getDescription())); + assertThat(savedScopes.get(2).getIcon(), equalTo(scope3.getIcon())); + assertThat(savedScopes.get(2).isDefaultScope(), equalTo(scope3.isDefaultScope())); + assertThat(savedScopes.get(2).isRestricted(), equalTo(scope3.isRestricted())); + assertThat(savedScopes.get(2).isStructured(), equalTo(scope3.isStructured())); + assertThat(savedScopes.get(2).getStructuredParamDescription(), equalTo(scope3.getStructuredParamDescription())); + + } + + @Test + public void testFixRefreshTokenAuthHolderReferencesOnImport() throws IOException, ParseException { + String expiration1 = "2014-09-10T22:49:44.090+0000"; + Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); + + ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); + when(mockedClient1.getClientId()).thenReturn("mocked_client_1"); + + OAuth2Request req1 = new OAuth2Request(new HashMap(), "client1", new ArrayList(), + true, new HashSet(), new HashSet(), "http://foo.com", + new HashSet(), null); + Authentication mockAuth1 = mock(Authentication.class, withSettings().serializable()); + OAuth2Authentication auth1 = new OAuth2Authentication(req1, mockAuth1); + + AuthenticationHolderEntity holder1 = new AuthenticationHolderEntity(); + holder1.setId(1L); + holder1.setAuthentication(auth1); + + OAuth2RefreshTokenEntity token1 = new OAuth2RefreshTokenEntity(); + token1.setId(1L); + token1.setClient(mockedClient1); + token1.setExpiration(expirationDate1); + token1.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.")); + token1.setAuthenticationHolder(holder1); + + String expiration2 = "2015-01-07T18:31:50.079+0000"; + Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); + + ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); + when(mockedClient2.getClientId()).thenReturn("mocked_client_2"); + + OAuth2Request req2 = new OAuth2Request(new HashMap(), "client2", new ArrayList(), + true, new HashSet(), new HashSet(), "http://bar.com", + new HashSet(), null); + Authentication mockAuth2 = mock(Authentication.class, withSettings().serializable()); + OAuth2Authentication auth2 = new OAuth2Authentication(req2, mockAuth2); + + AuthenticationHolderEntity holder2 = new AuthenticationHolderEntity(); + holder2.setId(2L); + holder2.setAuthentication(auth2); + + OAuth2RefreshTokenEntity token2 = new OAuth2RefreshTokenEntity(); + token2.setId(2L); + token2.setClient(mockedClient2); + token2.setExpiration(expirationDate2); + token2.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJlYmEyYjc3My0xNjAzLTRmNDAtOWQ3MS1hMGIxZDg1OWE2MDAifQ.")); + token2.setAuthenticationHolder(holder2); + + String configJson = "{" + + "\"" + MITREidDataService.SYSTEMSCOPES + "\": [], " + + "\"" + MITREidDataService.ACCESSTOKENS + "\": [], " + + "\"" + MITREidDataService.CLIENTS + "\": [], " + + "\"" + MITREidDataService.GRANTS + "\": [], " + + "\"" + MITREidDataService.WHITELISTEDSITES + "\": [], " + + "\"" + MITREidDataService.BLACKLISTEDSITES + "\": [], " + + "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [" + + + "{\"id\":1,\"authentication\":{\"authorizationRequest\":{\"clientId\":\"client1\",\"redirectUri\":\"http://foo.com\"}," + + "\"userAuthentication\":null}}," + + "{\"id\":2,\"authentication\":{\"authorizationRequest\":{\"clientId\":\"client2\",\"redirectUri\":\"http://bar.com\"}," + + "\"userAuthentication\":null}}" + + " ]," + + "\"" + MITREidDataService.REFRESHTOKENS + "\": [" + + + "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+0000\"," + + "\"authenticationHolderId\":1,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.\"}," + + "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+0000\"," + + "\"authenticationHolderId\":2,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJlYmEyYjc3My0xNjAzLTRmNDAtOWQ3MS1hMGIxZDg1OWE2MDAifQ.\"}" + + + " ]" + + "}"; + logger.debug(configJson); + + JsonReader reader = new JsonReader(new StringReader(configJson)); + final Map fakeRefreshTokenTable = new HashMap<>(); + final Map fakeAuthHolderTable = new HashMap<>(); + when(tokenRepository.saveRefreshToken(isA(OAuth2RefreshTokenEntity.class))).thenAnswer(new Answer() { + Long id = 343L; + @Override + public OAuth2RefreshTokenEntity answer(InvocationOnMock invocation) throws Throwable { + OAuth2RefreshTokenEntity _token = (OAuth2RefreshTokenEntity) invocation.getArguments()[0]; + if(_token.getId() == null) { + _token.setId(id++); + } + fakeRefreshTokenTable.put(_token.getId(), _token); + return _token; + } + }); + when(tokenRepository.getRefreshTokenById(anyLong())).thenAnswer(new Answer() { + @Override + public OAuth2RefreshTokenEntity answer(InvocationOnMock invocation) throws Throwable { + Long _id = (Long) invocation.getArguments()[0]; + return fakeRefreshTokenTable.get(_id); + } + }); + when(clientRepository.getClientByClientId(anyString())).thenAnswer(new Answer() { + @Override + public ClientDetailsEntity answer(InvocationOnMock invocation) throws Throwable { + String _clientId = (String) invocation.getArguments()[0]; + ClientDetailsEntity _client = mock(ClientDetailsEntity.class); + when(_client.getClientId()).thenReturn(_clientId); + return _client; + } + }); + when(authHolderRepository.save(isA(AuthenticationHolderEntity.class))).thenAnswer(new Answer() { + Long id = 356L; + @Override + public AuthenticationHolderEntity answer(InvocationOnMock invocation) throws Throwable { + AuthenticationHolderEntity _holder = (AuthenticationHolderEntity) invocation.getArguments()[0]; + if(_holder.getId() == null) { + _holder.setId(id++); + } + fakeAuthHolderTable.put(_holder.getId(), _holder); + return _holder; + } + }); + when(authHolderRepository.getById(anyLong())).thenAnswer(new Answer() { + @Override + public AuthenticationHolderEntity answer(InvocationOnMock invocation) throws Throwable { + Long _id = (Long) invocation.getArguments()[0]; + return fakeAuthHolderTable.get(_id); + } + }); + dataService.importData(reader); + + List savedRefreshTokens = new ArrayList(fakeRefreshTokenTable.values()); //capturedRefreshTokens.getAllValues(); + Collections.sort(savedRefreshTokens, new refreshTokenIdComparator()); + + assertThat(savedRefreshTokens.get(0).getAuthenticationHolder().getId(), equalTo(356L)); + assertThat(savedRefreshTokens.get(1).getAuthenticationHolder().getId(), equalTo(357L)); + } + + private Set jsonArrayToStringSet(JsonArray a) { + Set s = new HashSet<>(); + for (JsonElement jsonElement : a) { + s.add(jsonElement.getAsString()); + } + return s; + } + +} From 55b1b00b735029e90fb75fd8d77ad58801eafc93 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 9 Dec 2016 12:55:42 -0500 Subject: [PATCH 084/269] Updated relationship between approved sites and access tokens, closes #874 --- .../oauth2/model/OAuth2AccessTokenEntity.java | 15 ++++++++++++ .../repository/OAuth2TokenRepository.java | 3 +++ .../openid/connect/model/ApprovedSite.java | 15 ------------ .../connect/service/ApprovedSiteService.java | 9 +++++++ .../impl/JpaOAuth2TokenRepository.java | 9 +++++++ .../DefaultOAuth2ProviderTokenService.java | 13 ++++------ .../impl/DefaultApprovedSiteService.java | 10 +++++++- .../service/impl/MITREidDataService_1_0.java | 13 ++++++---- .../service/impl/MITREidDataService_1_1.java | 13 ++++++---- .../service/impl/MITREidDataService_1_2.java | 13 ++++++---- .../service/impl/MITREidDataService_1_3.java | 24 +++++++++++-------- 11 files changed, 88 insertions(+), 49 deletions(-) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java index ba7af670cc..3bff6639c1 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java @@ -47,6 +47,7 @@ import javax.persistence.Transient; import org.mitre.oauth2.model.convert.JWTStringConverter; +import org.mitre.openid.connect.model.ApprovedSite; import org.mitre.uma.model.Permission; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2AccessTokenJackson1Deserializer; @@ -70,6 +71,7 @@ @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_CLIENT, query = "select a from OAuth2AccessTokenEntity a where a.client = :" + OAuth2AccessTokenEntity.PARAM_CLIENT), @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_ID_TOKEN, query = "select a from OAuth2AccessTokenEntity a where a.idToken = :" + OAuth2AccessTokenEntity.PARAM_ID_TOKEN), @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_TOKEN_VALUE, query = "select a from OAuth2AccessTokenEntity a where a.jwt = :" + OAuth2AccessTokenEntity.PARAM_TOKEN_VALUE), + @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_APPROVED_SITE, query = "select a from OAuth2AccessTokenEntity a where a.approvedSite = :" + OAuth2AccessTokenEntity.PARAM_APPROVED_SITE), @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_RESOURCE_SET, query = "select a from OAuth2AccessTokenEntity a join a.permissions p where p.resourceSet.id = :" + OAuth2AccessTokenEntity.PARAM_RESOURCE_SET_ID) }) @org.codehaus.jackson.map.annotate.JsonSerialize(using = OAuth2AccessTokenJackson1Serializer.class) @@ -78,6 +80,7 @@ @com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = OAuth2AccessTokenJackson2Deserializer.class) public class OAuth2AccessTokenEntity implements OAuth2AccessToken { + public static final String QUERY_BY_APPROVED_SITE = "OAuth2AccessTokenEntity.getByApprovedSite"; public static final String QUERY_BY_TOKEN_VALUE = "OAuth2AccessTokenEntity.getByTokenValue"; public static final String QUERY_BY_ID_TOKEN = "OAuth2AccessTokenEntity.getByIdToken"; public static final String QUERY_BY_CLIENT = "OAuth2AccessTokenEntity.getByClient"; @@ -92,6 +95,7 @@ public class OAuth2AccessTokenEntity implements OAuth2AccessToken { public static final String PARAM_REFERSH_TOKEN = "refreshToken"; public static final String PARAM_DATE = "date"; public static final String PARAM_RESOURCE_SET_ID = "rsid"; + public static final String PARAM_APPROVED_SITE = "approvedSite"; public static final String ID_TOKEN_FIELD_NAME = "id_token"; @@ -114,6 +118,8 @@ public class OAuth2AccessTokenEntity implements OAuth2AccessToken { private Set scope; private Set permissions; + + private ApprovedSite approvedSite; /** * Create a new, blank access token @@ -337,4 +343,13 @@ public void setPermissions(Set permissions) { this.permissions = permissions; } + @ManyToOne + @JoinColumn(name="approved_site_id") + public ApprovedSite getApprovedSite() { + return approvedSite; + } + + public void setApprovedSite(ApprovedSite approvedSite) { + this.approvedSite = approvedSite; + } } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java index 5995875988..5073db4e28 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java @@ -22,6 +22,7 @@ import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.oauth2.model.OAuth2RefreshTokenEntity; +import org.mitre.openid.connect.model.ApprovedSite; import org.mitre.uma.model.ResourceSet; public interface OAuth2TokenRepository { @@ -65,5 +66,7 @@ public interface OAuth2TokenRepository { public void clearDuplicateAccessTokens(); public void clearDuplicateRefreshTokens(); + + public List getAccessTokensForApprovedSite(ApprovedSite approvedSite); } diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ApprovedSite.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ApprovedSite.java index 38ace61e9f..7f5733ef55 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ApprovedSite.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ApprovedSite.java @@ -81,9 +81,6 @@ public class ApprovedSite { // this should include all information for what data to access private Set allowedScopes; - //Link to any access tokens approved through this stored decision - private Set approvedAccessTokens = Sets.newHashSet(); - /** * Empty constructor */ @@ -229,16 +226,4 @@ public boolean isExpired() { } } - @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY) - @JoinColumn(name="approved_site_id") - public Set getApprovedAccessTokens() { - return approvedAccessTokens; - } - - /** - * @param approvedAccessTokens the approvedAccessTokens to set - */ - public void setApprovedAccessTokens(Set approvedAccessTokens) { - this.approvedAccessTokens = approvedAccessTokens; - } } diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java index a78322f141..78764e4951 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java @@ -18,8 +18,10 @@ import java.util.Collection; import java.util.Date; +import java.util.List; import java.util.Set; +import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.openid.connect.model.ApprovedSite; import org.springframework.security.oauth2.provider.ClientDetails; @@ -101,4 +103,11 @@ public interface ApprovedSiteService { * @return */ public void clearExpiredSites(); + + /** + * Return all approved access tokens for the site. + * @return + */ + public List getApprovedAccessTokens(ApprovedSite approvedSite); + } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java index 27bad5dc28..7d2bdb5a53 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java @@ -35,6 +35,7 @@ import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.oauth2.model.OAuth2RefreshTokenEntity; import org.mitre.oauth2.repository.OAuth2TokenRepository; +import org.mitre.openid.connect.model.ApprovedSite; import org.mitre.uma.model.ResourceSet; import org.mitre.util.jpa.JpaUtil; import org.slf4j.Logger; @@ -272,5 +273,13 @@ public void clearDuplicateRefreshTokens() { } } + + @Override + public List getAccessTokensForApprovedSite(ApprovedSite approvedSite) { + TypedQuery queryA = manager.createNamedQuery(OAuth2AccessTokenEntity.QUERY_BY_APPROVED_SITE, OAuth2AccessTokenEntity.class); + queryA.setParameter(OAuth2AccessTokenEntity.PARAM_APPROVED_SITE, approvedSite); + List accessTokens = queryA.getResultList(); + return accessTokens; + } } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java index ee66603f79..8d52c9d9bc 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java @@ -250,10 +250,6 @@ public OAuth2AccessTokenEntity createAccessToken(OAuth2Authentication authentica token.setRefreshToken(savedRefreshToken); } - OAuth2AccessTokenEntity enhancedToken = (OAuth2AccessTokenEntity) tokenEnhancer.enhance(token, authentication); - - OAuth2AccessTokenEntity savedToken = tokenRepository.saveAccessToken(enhancedToken); - //Add approved site reference, if any OAuth2Request originalAuthRequest = authHolder.getAuthentication().getOAuth2Request(); @@ -261,13 +257,14 @@ public OAuth2AccessTokenEntity createAccessToken(OAuth2Authentication authentica Long apId = Long.parseLong((String) originalAuthRequest.getExtensions().get("approved_site")); ApprovedSite ap = approvedSiteService.getById(apId); - Set apTokens = ap.getApprovedAccessTokens(); - apTokens.add(savedToken); - ap.setApprovedAccessTokens(apTokens); - approvedSiteService.save(ap); + token.setApprovedSite(ap); } + OAuth2AccessTokenEntity enhancedToken = (OAuth2AccessTokenEntity) tokenEnhancer.enhance(token, authentication); + + OAuth2AccessTokenEntity savedToken = tokenRepository.saveAccessToken(enhancedToken); + if (savedToken.getRefreshToken() != null) { tokenRepository.saveRefreshToken(savedToken.getRefreshToken()); // make sure we save any changes that might have been enhanced } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultApprovedSiteService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultApprovedSiteService.java index 9f9477d322..de8541c1fc 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultApprovedSiteService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultApprovedSiteService.java @@ -18,6 +18,7 @@ import java.util.Collection; import java.util.Date; +import java.util.List; import java.util.Set; import org.mitre.oauth2.model.OAuth2AccessTokenEntity; @@ -82,7 +83,7 @@ public ApprovedSite getById(Long id) { public void remove(ApprovedSite approvedSite) { //Remove any associated access and refresh tokens - Set accessTokens = approvedSite.getApprovedAccessTokens(); + List accessTokens = getApprovedAccessTokens(approvedSite); for (OAuth2AccessTokenEntity token : accessTokens) { if (token.getRefreshToken() != null) { @@ -180,4 +181,11 @@ private Collection getExpired() { return Collections2.filter(approvedSiteRepository.getAll(), isExpired); } + @Override + public List getApprovedAccessTokens( + ApprovedSite approvedSite) { + return tokenRepository.getAccessTokensForApprovedSite(approvedSite); + + } + } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java index 8ee5395b99..082b1f8b85 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java @@ -896,14 +896,17 @@ private void fixObjectReferences() { whitelistedSiteOldToNewIdMap.clear(); for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); - Set tokens = new HashSet<>(); + + Long newGrantId = grantOldToNewIdMap.get(oldGrantId); + ApprovedSite site = approvedSiteRepository.getById(newGrantId); + for(Long oldTokenId : oldAccessTokenIds) { Long newTokenId = accessTokenOldToNewIdMap.get(oldTokenId); - tokens.add(tokenRepository.getAccessTokenById(newTokenId)); + OAuth2AccessTokenEntity token = tokenRepository.getAccessTokenById(newTokenId); + token.setApprovedSite(site); + tokenRepository.saveAccessToken(token); } - Long newGrantId = grantOldToNewIdMap.get(oldGrantId); - ApprovedSite site = approvedSiteRepository.getById(newGrantId); - site.setApprovedAccessTokens(tokens); + approvedSiteRepository.save(site); } accessTokenOldToNewIdMap.clear(); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java index 53b884a554..b4ca8f5140 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java @@ -909,14 +909,17 @@ private void fixObjectReferences() { accessTokenToIdTokenRefs.clear(); for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); - Set tokens = new HashSet<>(); + + Long newGrantId = grantOldToNewIdMap.get(oldGrantId); + ApprovedSite site = approvedSiteRepository.getById(newGrantId); + for(Long oldTokenId : oldAccessTokenIds) { Long newTokenId = accessTokenOldToNewIdMap.get(oldTokenId); - tokens.add(tokenRepository.getAccessTokenById(newTokenId)); + OAuth2AccessTokenEntity token = tokenRepository.getAccessTokenById(newTokenId); + token.setApprovedSite(site); + tokenRepository.saveAccessToken(token); } - Long newGrantId = grantOldToNewIdMap.get(oldGrantId); - ApprovedSite site = approvedSiteRepository.getById(newGrantId); - site.setApprovedAccessTokens(tokens); + approvedSiteRepository.save(site); } accessTokenOldToNewIdMap.clear(); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java index bc8658ed85..0f0a9d3500 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java @@ -900,14 +900,17 @@ private void fixObjectReferences() { accessTokenToIdTokenRefs.clear(); for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); - Set tokens = new HashSet(); + + Long newGrantId = grantOldToNewIdMap.get(oldGrantId); + ApprovedSite site = approvedSiteRepository.getById(newGrantId); + for(Long oldTokenId : oldAccessTokenIds) { Long newTokenId = accessTokenOldToNewIdMap.get(oldTokenId); - tokens.add(tokenRepository.getAccessTokenById(newTokenId)); + OAuth2AccessTokenEntity token = tokenRepository.getAccessTokenById(newTokenId); + token.setApprovedSite(site); + tokenRepository.saveAccessToken(token); } - Long newGrantId = grantOldToNewIdMap.get(oldGrantId); - ApprovedSite site = approvedSiteRepository.getById(newGrantId); - site.setApprovedAccessTokens(tokens); + approvedSiteRepository.save(site); } accessTokenOldToNewIdMap.clear(); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java index 1bb9476d1d..38e1fdf93c 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java @@ -16,12 +16,17 @@ *******************************************************************************/ package org.mitre.openid.connect.service.impl; +import static org.mitre.util.JsonUtils.readMap; +import static org.mitre.util.JsonUtils.readSet; +import static org.mitre.util.JsonUtils.writeNullSafeArray; + import java.io.IOException; import java.io.Serializable; import java.text.ParseException; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -63,10 +68,6 @@ import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jwt.JWTParser; -import static org.mitre.util.JsonUtils.readMap; -import static org.mitre.util.JsonUtils.readSet; -import static org.mitre.util.JsonUtils.writeNullSafeArray; - /** * * Data service to import and export MITREid 1.2 configuration. @@ -367,7 +368,7 @@ private void writeGrants(JsonWriter writer) throws IOException { writer.name(USER_ID).value(site.getUserId()); writer.name(ALLOWED_SCOPES); writeNullSafeArray(writer, site.getAllowedScopes()); - Set tokens = site.getApprovedAccessTokens(); + List tokens = tokenRepository.getAccessTokensForApprovedSite(site); writer.name(APPROVED_ACCESS_TOKENS); writer.beginArray(); for (OAuth2AccessTokenEntity token : tokens) { @@ -1274,14 +1275,17 @@ private void fixObjectReferences() { accessTokenToIdTokenRefs.clear(); for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); - Set tokens = new HashSet(); + + Long newGrantId = grantOldToNewIdMap.get(oldGrantId); + ApprovedSite site = approvedSiteRepository.getById(newGrantId); + for(Long oldTokenId : oldAccessTokenIds) { Long newTokenId = accessTokenOldToNewIdMap.get(oldTokenId); - tokens.add(tokenRepository.getAccessTokenById(newTokenId)); + OAuth2AccessTokenEntity token = tokenRepository.getAccessTokenById(newTokenId); + token.setApprovedSite(site); + tokenRepository.saveAccessToken(token); } - Long newGrantId = grantOldToNewIdMap.get(oldGrantId); - ApprovedSite site = approvedSiteRepository.getById(newGrantId); - site.setApprovedAccessTokens(tokens); + approvedSiteRepository.save(site); } accessTokenOldToNewIdMap.clear(); From 22fa3605ef0dd9ea8712e7ca5e74bdd55be6d2b6 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 9 Dec 2016 12:56:06 -0500 Subject: [PATCH 085/269] Patched unit tests, still needs updates for checking approved site to token mapping on data import/export --- .../impl/TestDefaultApprovedSiteService.java | 10 ++++++++ .../impl/TestMITREidDataService_1_0.java | 6 ++--- .../impl/TestMITREidDataService_1_1.java | 4 +--- .../impl/TestMITREidDataService_1_2.java | 4 +--- .../impl/TestMITREidDataService_1_3.java | 18 +++----------- .../service/impl/MITREidDataService_1_2.java | 24 +++++++++++-------- 6 files changed, 32 insertions(+), 34 deletions(-) diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultApprovedSiteService.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultApprovedSiteService.java index 8bccacc962..debcbd964e 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultApprovedSiteService.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultApprovedSiteService.java @@ -16,13 +16,17 @@ *******************************************************************************/ package org.mitre.openid.connect.service.impl; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mitre.oauth2.model.ClientDetailsEntity; +import org.mitre.oauth2.model.OAuth2AccessTokenEntity; +import org.mitre.oauth2.repository.OAuth2TokenRepository; import org.mitre.openid.connect.model.ApprovedSite; import org.mitre.openid.connect.repository.ApprovedSiteRepository; import org.mitre.openid.connect.service.ApprovedSiteService; @@ -33,6 +37,7 @@ import org.mockito.runners.MockitoJUnitRunner; import org.springframework.test.annotation.Rollback; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import static org.mockito.Matchers.any; @@ -53,6 +58,9 @@ public class TestDefaultApprovedSiteService { @Mock private ApprovedSiteRepository repository; + @Mock + private OAuth2TokenRepository tokenRepository; + @Mock private StatsService statsService; @@ -97,6 +105,8 @@ public void prepare() { public void clearApprovedSitesForClient_success() { Set setToReturn = Sets.newHashSet(site2, site3); Mockito.when(repository.getByClientId(client.getClientId())).thenReturn(setToReturn); + List tokens = ImmutableList.of(); + Mockito.when(tokenRepository.getAccessTokensForApprovedSite(any(ApprovedSite.class))).thenReturn(tokens); service.clearApprovedSitesForClient(client); diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java index 5d097b619b..6574791b31 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java @@ -65,6 +65,7 @@ import org.springframework.security.oauth2.provider.OAuth2Request; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import com.nimbusds.jwt.JWTParser; @@ -588,7 +589,7 @@ public void testImportGrants() throws IOException, ParseException { site1.setAccessDate(accessDate1); site1.setUserId("user1"); site1.setAllowedScopes(ImmutableSet.of("openid", "phone")); - site1.setApprovedAccessTokens(ImmutableSet.of(mockToken1)); + when(mockToken1.getApprovedSite()).thenReturn(site1); Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+0000", Locale.ENGLISH); Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+0000", Locale.ENGLISH); @@ -665,6 +666,7 @@ public OAuth2AccessTokenEntity answer(InvocationOnMock invocation) throws Throwa return _token; } }); + when(tokenRepository.getAccessTokensForApprovedSite(site1)).thenReturn(Lists.newArrayList(mockToken1)); dataService.importData(reader); //2 for sites, 1 for updating access token ref on #1 @@ -679,14 +681,12 @@ public OAuth2AccessTokenEntity answer(InvocationOnMock invocation) throws Throwa assertThat(savedSites.get(0).getCreationDate(), equalTo(site1.getCreationDate())); assertThat(savedSites.get(0).getAllowedScopes(), equalTo(site1.getAllowedScopes())); assertThat(savedSites.get(0).getTimeoutDate(), equalTo(site1.getTimeoutDate())); - assertThat(savedSites.get(0).getApprovedAccessTokens().size(), equalTo(site1.getApprovedAccessTokens().size())); assertThat(savedSites.get(1).getClientId(), equalTo(site2.getClientId())); assertThat(savedSites.get(1).getAccessDate(), equalTo(site2.getAccessDate())); assertThat(savedSites.get(1).getCreationDate(), equalTo(site2.getCreationDate())); assertThat(savedSites.get(1).getAllowedScopes(), equalTo(site2.getAllowedScopes())); assertThat(savedSites.get(1).getTimeoutDate(), equalTo(site2.getTimeoutDate())); - assertThat(savedSites.get(1).getApprovedAccessTokens().size(), equalTo(site2.getApprovedAccessTokens().size())); } @Test diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java index 4b8427b059..2e80d6a8f7 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java @@ -592,7 +592,7 @@ public void testImportGrants() throws IOException, ParseException { site1.setAccessDate(accessDate1); site1.setUserId("user1"); site1.setAllowedScopes(ImmutableSet.of("openid", "phone")); - site1.setApprovedAccessTokens(ImmutableSet.of(mockToken1)); + when(mockToken1.getApprovedSite()).thenReturn(site1); Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+0000", Locale.ENGLISH); Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+0000", Locale.ENGLISH); @@ -683,14 +683,12 @@ public OAuth2AccessTokenEntity answer(InvocationOnMock invocation) throws Throwa assertThat(savedSites.get(0).getCreationDate(), equalTo(site1.getCreationDate())); assertThat(savedSites.get(0).getAllowedScopes(), equalTo(site1.getAllowedScopes())); assertThat(savedSites.get(0).getTimeoutDate(), equalTo(site1.getTimeoutDate())); - assertThat(savedSites.get(0).getApprovedAccessTokens().size(), equalTo(site1.getApprovedAccessTokens().size())); assertThat(savedSites.get(1).getClientId(), equalTo(site2.getClientId())); assertThat(savedSites.get(1).getAccessDate(), equalTo(site2.getAccessDate())); assertThat(savedSites.get(1).getCreationDate(), equalTo(site2.getCreationDate())); assertThat(savedSites.get(1).getAllowedScopes(), equalTo(site2.getAllowedScopes())); assertThat(savedSites.get(1).getTimeoutDate(), equalTo(site2.getTimeoutDate())); - assertThat(savedSites.get(1).getApprovedAccessTokens().size(), equalTo(site2.getApprovedAccessTokens().size())); } @Test diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java index 7073671cca..e12035fe25 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java @@ -607,7 +607,7 @@ public void testImportGrants() throws IOException, ParseException { site1.setAccessDate(accessDate1); site1.setUserId("user1"); site1.setAllowedScopes(ImmutableSet.of("openid", "phone")); - site1.setApprovedAccessTokens(ImmutableSet.of(mockToken1)); + when(mockToken1.getApprovedSite()).thenReturn(site1); Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+0000", Locale.ENGLISH); Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+0000", Locale.ENGLISH); @@ -698,14 +698,12 @@ public OAuth2AccessTokenEntity answer(InvocationOnMock invocation) throws Throwa assertThat(savedSites.get(0).getCreationDate(), equalTo(site1.getCreationDate())); assertThat(savedSites.get(0).getAllowedScopes(), equalTo(site1.getAllowedScopes())); assertThat(savedSites.get(0).getTimeoutDate(), equalTo(site1.getTimeoutDate())); - assertThat(savedSites.get(0).getApprovedAccessTokens().size(), equalTo(site1.getApprovedAccessTokens().size())); assertThat(savedSites.get(1).getClientId(), equalTo(site2.getClientId())); assertThat(savedSites.get(1).getAccessDate(), equalTo(site2.getAccessDate())); assertThat(savedSites.get(1).getCreationDate(), equalTo(site2.getCreationDate())); assertThat(savedSites.get(1).getAllowedScopes(), equalTo(site2.getAllowedScopes())); assertThat(savedSites.get(1).getTimeoutDate(), equalTo(site2.getTimeoutDate())); - assertThat(savedSites.get(1).getApprovedAccessTokens().size(), equalTo(site2.getApprovedAccessTokens().size())); } @Test diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java index 8ce49b7605..71fccf3bbf 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java @@ -1153,8 +1153,8 @@ public void testExportGrants() throws IOException, ParseException { site1.setAccessDate(accessDate1); site1.setUserId("user1"); site1.setAllowedScopes(ImmutableSet.of("openid", "phone")); - site1.setApprovedAccessTokens(ImmutableSet.of(mockToken1)); - + when(mockToken1.getApprovedSite()).thenReturn(site1); + Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+0000", Locale.ENGLISH); Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+0000", Locale.ENGLISH); Date timeoutDate2 = formatter.parse("2014-10-01T20:49:44.090+0000", Locale.ENGLISH); @@ -1246,16 +1246,6 @@ public void testExportGrants() throws IOException, ParseException { } assertThat(site.get("userId").getAsString(), equalTo(compare.getUserId())); assertThat(jsonArrayToStringSet(site.getAsJsonArray("allowedScopes")), equalTo(compare.getAllowedScopes())); - if (site.get("approvedAccessTokens").isJsonNull() || site.getAsJsonArray("approvedAccessTokens") == null) { - assertTrue(compare.getApprovedAccessTokens() == null || compare.getApprovedAccessTokens().isEmpty()); - } else { - assertNotNull(compare.getApprovedAccessTokens()); - Set tokenIds = new HashSet<>(); - for(OAuth2AccessTokenEntity entity : compare.getApprovedAccessTokens()) { - tokenIds.add(entity.getId().toString()); - } - assertThat(jsonArrayToStringSet(site.getAsJsonArray("approvedAccessTokens")), equalTo(tokenIds)); - } checked.add(compare); } } @@ -1278,7 +1268,7 @@ public void testImportGrants() throws IOException, ParseException { site1.setAccessDate(accessDate1); site1.setUserId("user1"); site1.setAllowedScopes(ImmutableSet.of("openid", "phone")); - site1.setApprovedAccessTokens(ImmutableSet.of(mockToken1)); + when(mockToken1.getApprovedSite()).thenReturn(site1); Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+0000", Locale.ENGLISH); Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+0000", Locale.ENGLISH); @@ -1369,14 +1359,12 @@ public OAuth2AccessTokenEntity answer(InvocationOnMock invocation) throws Throwa assertThat(savedSites.get(0).getCreationDate(), equalTo(site1.getCreationDate())); assertThat(savedSites.get(0).getAllowedScopes(), equalTo(site1.getAllowedScopes())); assertThat(savedSites.get(0).getTimeoutDate(), equalTo(site1.getTimeoutDate())); - assertThat(savedSites.get(0).getApprovedAccessTokens().size(), equalTo(site1.getApprovedAccessTokens().size())); assertThat(savedSites.get(1).getClientId(), equalTo(site2.getClientId())); assertThat(savedSites.get(1).getAccessDate(), equalTo(site2.getAccessDate())); assertThat(savedSites.get(1).getCreationDate(), equalTo(site2.getCreationDate())); assertThat(savedSites.get(1).getAllowedScopes(), equalTo(site2.getAllowedScopes())); assertThat(savedSites.get(1).getTimeoutDate(), equalTo(site2.getTimeoutDate())); - assertThat(savedSites.get(1).getApprovedAccessTokens().size(), equalTo(site2.getApprovedAccessTokens().size())); } @Test diff --git a/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java b/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java index 43ca7b07a3..73ddddecbb 100644 --- a/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java +++ b/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java @@ -17,12 +17,17 @@ *******************************************************************************/ +import static org.mitre.util.JsonUtils.readMap; +import static org.mitre.util.JsonUtils.readSet; +import static org.mitre.util.JsonUtils.writeNullSafeArray; + import java.io.IOException; import java.io.Serializable; import java.text.ParseException; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -76,10 +81,6 @@ import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jwt.JWTParser; -import static org.mitre.util.JsonUtils.readMap; -import static org.mitre.util.JsonUtils.readSet; -import static org.mitre.util.JsonUtils.writeNullSafeArray; - /** * * UMA EXPORT OVERRIDE @@ -567,7 +568,7 @@ private void writeGrants(JsonWriter writer) throws IOException { writer.name(USER_ID).value(site.getUserId()); writer.name(ALLOWED_SCOPES); writeNullSafeArray(writer, site.getAllowedScopes()); - Set tokens = site.getApprovedAccessTokens(); + List tokens = tokenRepository.getAccessTokensForApprovedSite(site); writer.name(APPROVED_ACCESS_TOKENS); writer.beginArray(); for (OAuth2AccessTokenEntity token : tokens) { @@ -1791,14 +1792,17 @@ private void fixObjectReferences() { accessTokenToIdTokenRefs.clear(); for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); - Set tokens = new HashSet(); + + Long newGrantId = grantOldToNewIdMap.get(oldGrantId); + ApprovedSite site = approvedSiteRepository.getById(newGrantId); + for(Long oldTokenId : oldAccessTokenIds) { Long newTokenId = accessTokenOldToNewIdMap.get(oldTokenId); - tokens.add(tokenRepository.getAccessTokenById(newTokenId)); + OAuth2AccessTokenEntity token = tokenRepository.getAccessTokenById(newTokenId); + token.setApprovedSite(site); + tokenRepository.saveAccessToken(token); } - Long newGrantId = grantOldToNewIdMap.get(oldGrantId); - ApprovedSite site = approvedSiteRepository.getById(newGrantId); - site.setApprovedAccessTokens(tokens); + approvedSiteRepository.save(site); } accessTokenOldToNewIdMap.clear(); From 375a5f2e4767c36af6d6da0d1fddca2632f5282e Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 9 Dec 2016 15:10:07 -0500 Subject: [PATCH 086/269] removed token count from grant admin page --- .../src/main/webapp/resources/template/grant.html | 1 - 1 file changed, 1 deletion(-) diff --git a/openid-connect-server-webapp/src/main/webapp/resources/template/grant.html b/openid-connect-server-webapp/src/main/webapp/resources/template/grant.html index 47b05e4912..f42c018c8c 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/template/grant.html +++ b/openid-connect-server-webapp/src/main/webapp/resources/template/grant.html @@ -57,7 +57,6 @@ <% if (client.dynamicallyRegistered) { %> <% } %> - <%- grant.approvedAccessTokens.length %> From 4f4c8de1c8f662ec74e4b4c99ec06a23c0cb66c5 Mon Sep 17 00:00:00 2001 From: Mikko Tommila Date: Fri, 23 Oct 2015 15:03:01 +0300 Subject: [PATCH 087/269] Fix JPA issues to allow using Hibernate --- .../model/AuthenticationHolderEntity.java | 56 ++++++------------- .../oauth2/model/OAuth2AccessTokenEntity.java | 2 +- .../oauth2/model/SavedUserAuthentication.java | 14 ++--- .../impl/JpaOAuth2TokenRepository.java | 12 +++- 4 files changed, 31 insertions(+), 53 deletions(-) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java index 961b924200..a5c570d682 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java @@ -66,7 +66,7 @@ public class AuthenticationHolderEntity { private SavedUserAuthentication userAuth; - private Collection authorities; + private Collection authorities; private Set resourceIds; @@ -116,14 +116,14 @@ public void setAuthentication(OAuth2Authentication authentication) { // pull apart the request and save its bits OAuth2Request o2Request = authentication.getOAuth2Request(); - setAuthorities(o2Request.getAuthorities()); + setAuthorities(o2Request.getAuthorities() == null ? null : new HashSet<>(o2Request.getAuthorities())); setClientId(o2Request.getClientId()); - setExtensions(o2Request.getExtensions()); + setExtensions(o2Request.getExtensions() == null ? null : new HashMap<>(o2Request.getExtensions())); setRedirectUri(o2Request.getRedirectUri()); - setRequestParameters(o2Request.getRequestParameters()); - setResourceIds(o2Request.getResourceIds()); - setResponseTypes(o2Request.getResponseTypes()); - setScope(o2Request.getScope()); + setRequestParameters(o2Request.getRequestParameters() == null ? null : new HashMap<>(o2Request.getRequestParameters())); + setResourceIds(o2Request.getResourceIds() == null ? null : new HashSet<>(o2Request.getResourceIds())); + setResponseTypes(o2Request.getResponseTypes() == null ? null : new HashSet<>(o2Request.getResponseTypes())); + setScope(o2Request.getScope() == null ? null : new HashSet<>(o2Request.getScope())); setApproved(o2Request.isApproved()); if (authentication.getUserAuthentication() != null) { @@ -159,19 +159,15 @@ public void setUserAuth(SavedUserAuthentication userAuth) { ) @Convert(converter = SimpleGrantedAuthorityStringConverter.class) @Column(name="authority") - public Collection getAuthorities() { + public Collection getAuthorities() { return authorities; } /** * @param authorities the authorities to set */ - public void setAuthorities(Collection authorities) { - if (authorities != null) { - this.authorities = new HashSet<>(authorities); - } else { - this.authorities = null; - } + public void setAuthorities(Collection authorities) { + this.authorities = authorities; } /** @@ -191,11 +187,7 @@ public Set getResourceIds() { * @param resourceIds the resourceIds to set */ public void setResourceIds(Set resourceIds) { - if (resourceIds != null) { - this.resourceIds = new HashSet<>(resourceIds); - } else { - this.resourceIds = null; - } + this.resourceIds = resourceIds; } /** @@ -247,11 +239,7 @@ public Set getResponseTypes() { * @param responseTypes the responseTypes to set */ public void setResponseTypes(Set responseTypes) { - if (responseTypes != null) { - this.responseTypes = new HashSet<>(responseTypes); - } else { - this.responseTypes = null; - } + this.responseTypes = responseTypes; } /** @@ -264,7 +252,7 @@ public void setResponseTypes(Set responseTypes) { ) @Column(name="val") @MapKeyColumn(name="extension") - @Convert(converter=SerializableStringConverter.class) + @Convert(attributeName="value", converter=SerializableStringConverter.class) public Map getExtensions() { return extensions; } @@ -273,11 +261,7 @@ public Map getExtensions() { * @param extensions the extensions to set */ public void setExtensions(Map extensions) { - if (extensions != null) { - this.extensions = new HashMap<>(extensions); - } else { - this.extensions = null; - } + this.extensions = extensions; } /** @@ -313,11 +297,7 @@ public Set getScope() { * @param scope the scope to set */ public void setScope(Set scope) { - if (scope != null) { - this.scope = new HashSet<>(scope); - } else { - this.scope = null; - } + this.scope = scope; } /** @@ -338,11 +318,7 @@ public Map getRequestParameters() { * @param requestParameters the requestParameters to set */ public void setRequestParameters(Map requestParameters) { - if (requestParameters != null) { - this.requestParameters = new HashMap<>(requestParameters); - } else { - this.requestParameters = null; - } + this.requestParameters = requestParameters; } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java index 3bff6639c1..9ba5a03e2f 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java @@ -265,7 +265,7 @@ public boolean isExpired() { /** * @return the idToken */ - @OneToOne(cascade=CascadeType.ALL) // one-to-one mapping for now + @OneToOne(cascade=CascadeType.ALL, orphanRemoval=true) // one-to-one mapping for now @JoinColumn(name = "id_token_id") public OAuth2AccessTokenEntity getIdToken() { return idToken; diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/SavedUserAuthentication.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/SavedUserAuthentication.java index c83859fc55..bf242bb1b8 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/SavedUserAuthentication.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/SavedUserAuthentication.java @@ -54,7 +54,7 @@ public class SavedUserAuthentication implements Authentication { private String name; - private Collection authorities; + private Collection authorities; private boolean authenticated; @@ -65,7 +65,7 @@ public class SavedUserAuthentication implements Authentication { */ public SavedUserAuthentication(Authentication src) { setName(src.getName()); - setAuthorities(src.getAuthorities()); + setAuthorities(new HashSet<>(src.getAuthorities())); setAuthenticated(src.isAuthenticated()); if (src instanceof SavedUserAuthentication) { @@ -115,7 +115,7 @@ public String getName() { ) @Convert(converter = SimpleGrantedAuthorityStringConverter.class) @Column(name="authority") - public Collection getAuthorities() { + public Collection getAuthorities() { return authorities; } @@ -175,12 +175,8 @@ public void setName(String name) { /** * @param authorities the authorities to set */ - public void setAuthorities(Collection authorities) { - if (authorities != null) { - this.authorities = new HashSet<>(authorities); - } else { - this.authorities = null; - } + public void setAuthorities(Collection authorities) { + this.authorities = authorities; } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java index 7d2bdb5a53..5317a31946 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java @@ -97,7 +97,13 @@ public OAuth2AccessTokenEntity saveAccessToken(OAuth2AccessTokenEntity token) { public void removeAccessToken(OAuth2AccessTokenEntity accessToken) { OAuth2AccessTokenEntity found = getAccessTokenByValue(accessToken.getValue()); if (found != null) { - manager.remove(found); + OAuth2AccessTokenEntity accessTokenForIdToken = getAccessTokenForIdToken(found); + if (accessTokenForIdToken != null) { + accessTokenForIdToken.setIdToken(null); + JpaUtil.saveOrUpdate(accessTokenForIdToken.getId(), manager, accessTokenForIdToken); + } else { + manager.remove(found); + } } else { throw new IllegalArgumentException("Access token not found: " + accessToken); } @@ -231,7 +237,7 @@ public Set getAccessTokensForResourceSet(ResourceSet rs @Transactional(value="defaultTransactionManager") public void clearDuplicateAccessTokens() { - Query query = manager.createQuery("select a.jwt, count(1) as c from OAuth2AccessTokenEntity a GROUP BY a.jwt HAVING c > 1"); + Query query = manager.createQuery("select a.jwt, count(1) as c from OAuth2AccessTokenEntity a GROUP BY a.jwt HAVING count(1) > 1"); @SuppressWarnings("unchecked") List resultList = query.getResultList(); List values = new ArrayList<>(); @@ -255,7 +261,7 @@ public void clearDuplicateAccessTokens() { @Override @Transactional(value="defaultTransactionManager") public void clearDuplicateRefreshTokens() { - Query query = manager.createQuery("select a.jwt, count(1) as c from OAuth2RefreshTokenEntity a GROUP BY a.jwt HAVING c > 1"); + Query query = manager.createQuery("select a.jwt, count(1) as c from OAuth2RefreshTokenEntity a GROUP BY a.jwt HAVING count(1) > 1"); @SuppressWarnings("unchecked") List resultList = query.getResultList(); List values = new ArrayList<>(); From 91ed758ed14aedc5d8b476c4dbded492bfed19ec Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 9 Dec 2016 16:58:43 -0500 Subject: [PATCH 088/269] =?UTF-8?q?removed=20=E2=80=9CattributeName?= =?UTF-8?q?=E2=80=9D=20from=20annotation=20as=20this=20breaks=20EclipseLin?= =?UTF-8?q?k=20JPA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/mitre/oauth2/model/AuthenticationHolderEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java index a5c570d682..be334f4098 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java @@ -252,7 +252,7 @@ public void setResponseTypes(Set responseTypes) { ) @Column(name="val") @MapKeyColumn(name="extension") - @Convert(attributeName="value", converter=SerializableStringConverter.class) + @Convert(converter=SerializableStringConverter.class) public Map getExtensions() { return extensions; } From 91da3935f575565140f0b648c9d647a94cd2f5d8 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 9 Dec 2016 16:59:15 -0500 Subject: [PATCH 089/269] =?UTF-8?q?Made=20ID=20tokens=20ephemeral,=20made?= =?UTF-8?q?=20access=20token=E2=80=99s=20=E2=80=9Cadditional=20information?= =?UTF-8?q?=E2=80=9D=20extensible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/model/OAuth2AccessTokenEntity.java | 55 ++++++------------- .../repository/OAuth2TokenRepository.java | 2 - .../service/OAuth2TokenEntityService.java | 6 -- .../oauth2/service/SystemScopeService.java | 2 - .../connect/service/OIDCTokenService.java | 4 +- .../db/hsql/hsql_database_tables.sql | 1 - .../impl/JpaOAuth2TokenRepository.java | 19 +------ .../DefaultOAuth2ProviderTokenService.java | 20 +++---- .../org/mitre/oauth2/view/TokenApiView.java | 1 - .../service/impl/DefaultOIDCTokenService.java | 28 +++------- .../service/impl/MITREidDataService_1_0.java | 17 ------ .../service/impl/MITREidDataService_1_1.java | 17 ------ .../service/impl/MITREidDataService_1_2.java | 18 ------ .../service/impl/MITREidDataService_1_3.java | 20 ------- .../connect/token/ConnectTokenEnhancer.java | 5 +- .../impl/TestMITREidDataService_1_0.java | 5 +- .../impl/TestMITREidDataService_1_1.java | 5 +- .../impl/TestMITREidDataService_1_2.java | 5 +- .../impl/TestMITREidDataService_1_3.java | 13 +---- .../service/impl/MITREidDataService_1_2.java | 12 +--- 20 files changed, 50 insertions(+), 205 deletions(-) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java index 9ba5a03e2f..77414c53e4 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java @@ -69,7 +69,6 @@ @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_EXPIRED_BY_DATE, query = "select a from OAuth2AccessTokenEntity a where a.expiration <= :" + OAuth2AccessTokenEntity.PARAM_DATE), @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_REFRESH_TOKEN, query = "select a from OAuth2AccessTokenEntity a where a.refreshToken = :" + OAuth2AccessTokenEntity.PARAM_REFERSH_TOKEN), @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_CLIENT, query = "select a from OAuth2AccessTokenEntity a where a.client = :" + OAuth2AccessTokenEntity.PARAM_CLIENT), - @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_ID_TOKEN, query = "select a from OAuth2AccessTokenEntity a where a.idToken = :" + OAuth2AccessTokenEntity.PARAM_ID_TOKEN), @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_TOKEN_VALUE, query = "select a from OAuth2AccessTokenEntity a where a.jwt = :" + OAuth2AccessTokenEntity.PARAM_TOKEN_VALUE), @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_APPROVED_SITE, query = "select a from OAuth2AccessTokenEntity a where a.approvedSite = :" + OAuth2AccessTokenEntity.PARAM_APPROVED_SITE), @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_RESOURCE_SET, query = "select a from OAuth2AccessTokenEntity a join a.permissions p where p.resourceSet.id = :" + OAuth2AccessTokenEntity.PARAM_RESOURCE_SET_ID) @@ -82,7 +81,6 @@ public class OAuth2AccessTokenEntity implements OAuth2AccessToken { public static final String QUERY_BY_APPROVED_SITE = "OAuth2AccessTokenEntity.getByApprovedSite"; public static final String QUERY_BY_TOKEN_VALUE = "OAuth2AccessTokenEntity.getByTokenValue"; - public static final String QUERY_BY_ID_TOKEN = "OAuth2AccessTokenEntity.getByIdToken"; public static final String QUERY_BY_CLIENT = "OAuth2AccessTokenEntity.getByClient"; public static final String QUERY_BY_REFRESH_TOKEN = "OAuth2AccessTokenEntity.getByRefreshToken"; public static final String QUERY_EXPIRED_BY_DATE = "OAuth2AccessTokenEntity.getAllExpiredByDate"; @@ -90,7 +88,6 @@ public class OAuth2AccessTokenEntity implements OAuth2AccessToken { public static final String QUERY_BY_RESOURCE_SET = "OAuth2AccessTokenEntity.getByResourceSet"; public static final String PARAM_TOKEN_VALUE = "tokenValue"; - public static final String PARAM_ID_TOKEN = "idToken"; public static final String PARAM_CLIENT = "client"; public static final String PARAM_REFERSH_TOKEN = "refreshToken"; public static final String PARAM_DATE = "date"; @@ -107,8 +104,6 @@ public class OAuth2AccessTokenEntity implements OAuth2AccessToken { private JWT jwtValue; // JWT-encoded access token value - private OAuth2AccessTokenEntity idToken; // JWT-encoded OpenID Connect IdToken - private Date expiration; private String tokenType = OAuth2AccessToken.BEARER_TYPE; @@ -120,6 +115,8 @@ public class OAuth2AccessTokenEntity implements OAuth2AccessToken { private Set permissions; private ApprovedSite approvedSite; + + private Map additionalInformation = new HashMap<>(); // ephemeral map of items to be added to the OAuth token response /** * Create a new, blank access token @@ -146,16 +143,13 @@ public void setId(Long id) { } /** - * Get all additional information to be sent to the serializer. Inserts a copy of the IdToken (in JWT String form). + * Get all additional information to be sent to the serializer as part of the token response. + * This map is not persisted to the database. */ @Override @Transient public Map getAdditionalInformation() { - Map map = new HashMap<>(); //super.getAdditionalInformation(); - if (getIdToken() != null) { - map.put(ID_TOKEN_FIELD_NAME, getIdTokenString()); - } - return map; + return additionalInformation; } /** @@ -262,34 +256,6 @@ public boolean isExpired() { return getExpiration() == null ? false : System.currentTimeMillis() > getExpiration().getTime(); } - /** - * @return the idToken - */ - @OneToOne(cascade=CascadeType.ALL, orphanRemoval=true) // one-to-one mapping for now - @JoinColumn(name = "id_token_id") - public OAuth2AccessTokenEntity getIdToken() { - return idToken; - } - - /** - * @param idToken the idToken to set - */ - public void setIdToken(OAuth2AccessTokenEntity idToken) { - this.idToken = idToken; - } - - /** - * @return the idTokenString - */ - @Transient - public String getIdTokenString() { - if (idToken != null) { - return idToken.getValue(); // get the JWT string value of the id token entity - } else { - return null; - } - } - /** * @return the jwtValue */ @@ -352,4 +318,15 @@ public ApprovedSite getApprovedSite() { public void setApprovedSite(ApprovedSite approvedSite) { this.approvedSite = approvedSite; } + + /** + * Add the ID Token to the additionalInformation map for a token response. + * @param idToken + */ + @Transient + public void setIdToken(JWT idToken) { + if (idToken != null) { + additionalInformation.put(ID_TOKEN_FIELD_NAME, idToken.serialize()); + } + } } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java index 5073db4e28..79f167c511 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java @@ -51,8 +51,6 @@ public interface OAuth2TokenRepository { public List getRefreshTokensForClient(ClientDetailsEntity client); - public OAuth2AccessTokenEntity getAccessTokenForIdToken(OAuth2AccessTokenEntity idToken); - public Set getAllAccessTokens(); public Set getAllRefreshTokens(); diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/OAuth2TokenEntityService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/OAuth2TokenEntityService.java index da9eb6b3ad..3367ce3a9a 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/OAuth2TokenEntityService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/OAuth2TokenEntityService.java @@ -50,12 +50,6 @@ public interface OAuth2TokenEntityService extends AuthorizationServerTokenServic @Override public OAuth2AccessTokenEntity getAccessToken(OAuth2Authentication authentication); - /** - * @param incomingToken - * @return - */ - public OAuth2AccessTokenEntity getAccessTokenForIdToken(OAuth2AccessTokenEntity idToken); - public OAuth2AccessTokenEntity getAccessTokenById(Long id); public OAuth2RefreshTokenEntity getRefreshTokenById(Long id); diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/SystemScopeService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/SystemScopeService.java index 84cdbdaf87..1b4298634b 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/SystemScopeService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/SystemScopeService.java @@ -33,7 +33,6 @@ public interface SystemScopeService { public static final String OFFLINE_ACCESS = "offline_access"; public static final String OPENID_SCOPE = "openid"; - public static final String ID_TOKEN_SCOPE = "id-token"; // ID tokens are generated using this scope public static final String REGISTRATION_TOKEN_SCOPE = "registration-token"; // this scope manages dynamic client registrations public static final String RESOURCE_TOKEN_SCOPE = "resource-token"; // this scope manages client-style protected resources public static final String UMA_PROTECTION_SCOPE = "uma_protection"; @@ -41,7 +40,6 @@ public interface SystemScopeService { public static final Set reservedScopes = Sets.newHashSet( - new SystemScope(ID_TOKEN_SCOPE), new SystemScope(REGISTRATION_TOKEN_SCOPE), new SystemScope(RESOURCE_TOKEN_SCOPE) ); diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/OIDCTokenService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/OIDCTokenService.java index f9e044bdf1..2c0e57c30c 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/OIDCTokenService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/OIDCTokenService.java @@ -22,6 +22,8 @@ import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.springframework.security.oauth2.provider.OAuth2Request; +import com.nimbusds.jwt.JWT; + /** * Service to create specialty OpenID Connect tokens. * @@ -41,7 +43,7 @@ public interface OIDCTokenService { * @param accessToken * @return */ - public OAuth2AccessTokenEntity createIdToken( + public JWT createIdToken( ClientDetailsEntity client, OAuth2Request request, Date issueTime, String sub, OAuth2AccessTokenEntity accessToken); diff --git a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql index 382a1934c3..50108c4db7 100644 --- a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql @@ -10,7 +10,6 @@ CREATE TABLE IF NOT EXISTS access_token ( refresh_token_id BIGINT, client_id BIGINT, auth_holder_id BIGINT, - id_token_id BIGINT, approved_site_id BIGINT ); diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java index 5317a31946..eb30cbe455 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java @@ -97,13 +97,7 @@ public OAuth2AccessTokenEntity saveAccessToken(OAuth2AccessTokenEntity token) { public void removeAccessToken(OAuth2AccessTokenEntity accessToken) { OAuth2AccessTokenEntity found = getAccessTokenByValue(accessToken.getValue()); if (found != null) { - OAuth2AccessTokenEntity accessTokenForIdToken = getAccessTokenForIdToken(found); - if (accessTokenForIdToken != null) { - accessTokenForIdToken.setIdToken(null); - JpaUtil.saveOrUpdate(accessTokenForIdToken.getId(), manager, accessTokenForIdToken); - } else { - manager.remove(found); - } + manager.remove(found); } else { throw new IllegalArgumentException("Access token not found: " + accessToken); } @@ -193,17 +187,6 @@ public List getRefreshTokensForClient(ClientDetailsEnt return refreshTokens; } - /* (non-Javadoc) - * @see org.mitre.oauth2.repository.OAuth2TokenRepository#getAccessTokenForIdToken(org.mitre.oauth2.model.OAuth2AccessTokenEntity) - */ - @Override - public OAuth2AccessTokenEntity getAccessTokenForIdToken(OAuth2AccessTokenEntity idToken) { - TypedQuery queryA = manager.createNamedQuery(OAuth2AccessTokenEntity.QUERY_BY_ID_TOKEN, OAuth2AccessTokenEntity.class); - queryA.setParameter(OAuth2AccessTokenEntity.PARAM_ID_TOKEN, idToken); - List accessTokens = queryA.getResultList(); - return JpaUtil.getSingleResult(accessTokens); - } - @Override public Set getAllExpiredAccessTokens() { TypedQuery query = manager.createNamedQuery(OAuth2AccessTokenEntity.QUERY_EXPIRED_BY_DATE, OAuth2AccessTokenEntity.class); diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java index 8d52c9d9bc..b358712181 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java @@ -263,7 +263,7 @@ public OAuth2AccessTokenEntity createAccessToken(OAuth2Authentication authentica OAuth2AccessTokenEntity enhancedToken = (OAuth2AccessTokenEntity) tokenEnhancer.enhance(token, authentication); - OAuth2AccessTokenEntity savedToken = tokenRepository.saveAccessToken(enhancedToken); + OAuth2AccessTokenEntity savedToken = saveAccessToken(enhancedToken); if (savedToken.getRefreshToken() != null) { tokenRepository.saveRefreshToken(savedToken.getRefreshToken()); // make sure we save any changes that might have been enhanced @@ -542,7 +542,14 @@ private Collection getOrphanedAuthenticationHolders( */ @Override public OAuth2AccessTokenEntity saveAccessToken(OAuth2AccessTokenEntity accessToken) { - return tokenRepository.saveAccessToken(accessToken); + OAuth2AccessTokenEntity newToken = tokenRepository.saveAccessToken(accessToken); + + // if the old token has any additional information for the return from the token endpoint, carry it through here after save + if (accessToken.getAdditionalInformation() != null && !accessToken.getAdditionalInformation().isEmpty()) { + newToken.getAdditionalInformation().putAll(accessToken.getAdditionalInformation()); + } + + return newToken; } /* (non-Javadoc) @@ -567,15 +574,6 @@ public void setTokenEnhancer(TokenEnhancer tokenEnhancer) { this.tokenEnhancer = tokenEnhancer; } - /* (non-Javadoc) - * @see org.mitre.oauth2.service.OAuth2TokenEntityService#getAccessTokenForIdToken(org.mitre.oauth2.model.OAuth2AccessTokenEntity) - */ - @Override - public OAuth2AccessTokenEntity getAccessTokenForIdToken(OAuth2AccessTokenEntity idToken) { - return tokenRepository.getAccessTokenForIdToken(idToken); - } - - @Override public OAuth2AccessTokenEntity getRegistrationAccessTokenForClient(ClientDetailsEntity client) { List allTokens = getAccessTokensForClient(client); diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/view/TokenApiView.java b/openid-connect-server/src/main/java/org/mitre/oauth2/view/TokenApiView.java index aac25f3018..071675647c 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/view/TokenApiView.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/view/TokenApiView.java @@ -84,7 +84,6 @@ public JsonElement serialize(OAuth2AccessTokenEntity src, o.addProperty("value", src.getValue()); o.addProperty("id", src.getId()); - o.addProperty("idTokenId", src.getIdToken() != null ? src.getIdToken().getId() : null); o.addProperty("refreshTokenId", src.getRefreshToken() != null ? src.getRefreshToken().getId() : null); o.add("scopes", context.serialize(src.getScope())); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultOIDCTokenService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultOIDCTokenService.java index 1ec1fa914a..fef7a64a8b 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultOIDCTokenService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultOIDCTokenService.java @@ -53,6 +53,7 @@ import com.google.common.collect.Sets; import com.nimbusds.jose.Algorithm; import com.nimbusds.jose.JWEHeader; +import com.nimbusds.jose.JWEObject; import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSHeader; import com.nimbusds.jose.util.Base64URL; @@ -94,7 +95,7 @@ public class DefaultOIDCTokenService implements OIDCTokenService { private OAuth2TokenEntityService tokenService; @Override - public OAuth2AccessTokenEntity createIdToken(ClientDetailsEntity client, OAuth2Request request, Date issueTime, String sub, OAuth2AccessTokenEntity accessToken) { + public JWT createIdToken(ClientDetailsEntity client, OAuth2Request request, Date issueTime, String sub, OAuth2AccessTokenEntity accessToken) { JWSAlgorithm signingAlg = jwtService.getDefaultSigningAlgorithm(); @@ -103,7 +104,8 @@ public OAuth2AccessTokenEntity createIdToken(ClientDetailsEntity client, OAuth2R } - OAuth2AccessTokenEntity idTokenEntity = new OAuth2AccessTokenEntity(); + JWT idToken = null; + JWTClaimsSet.Builder idClaims = new JWTClaimsSet.Builder(); // if the auth time claim was explicitly requested OR if the client always wants the auth time, put it in @@ -128,7 +130,6 @@ public OAuth2AccessTokenEntity createIdToken(ClientDetailsEntity client, OAuth2R if (client.getIdTokenValiditySeconds() != null) { Date expiration = new Date(System.currentTimeMillis() + (client.getIdTokenValiditySeconds() * 1000L)); idClaims.expirationTime(expiration); - idTokenEntity.setExpiration(expiration); } idClaims.issuer(configBean.getIssuer()); @@ -157,11 +158,9 @@ public OAuth2AccessTokenEntity createIdToken(ClientDetailsEntity client, OAuth2R if (encrypter != null) { - EncryptedJWT idToken = new EncryptedJWT(new JWEHeader(client.getIdTokenEncryptedResponseAlg(), client.getIdTokenEncryptedResponseEnc()), idClaims.build()); - - encrypter.encryptJwt(idToken); + idToken = new EncryptedJWT(new JWEHeader(client.getIdTokenEncryptedResponseAlg(), client.getIdTokenEncryptedResponseEnc()), idClaims.build()); - idTokenEntity.setJwt(idToken); + encrypter.encryptJwt((JWEObject) idToken); } else { logger.error("Couldn't find encrypter for client: " + client.getClientId()); @@ -169,8 +168,6 @@ public OAuth2AccessTokenEntity createIdToken(ClientDetailsEntity client, OAuth2R } else { - JWT idToken; - if (signingAlg.equals(Algorithm.NONE)) { // unsigned ID token idToken = new PlainJWT(idClaims.build()); @@ -206,20 +203,9 @@ public OAuth2AccessTokenEntity createIdToken(ClientDetailsEntity client, OAuth2R } } - - idTokenEntity.setJwt(idToken); } - idTokenEntity.setAuthenticationHolder(accessToken.getAuthenticationHolder()); - - // create a scope set with just the special "id-token" scope - //Set idScopes = new HashSet(token.getScope()); // this would copy the original token's scopes in, we don't really want that - Set idScopes = Sets.newHashSet(SystemScopeService.ID_TOKEN_SCOPE); - idTokenEntity.setScope(idScopes); - - idTokenEntity.setClient(accessToken.getClient()); - - return idTokenEntity; + return idToken; } /** diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java index 082b1f8b85..37d3747e54 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java @@ -223,7 +223,6 @@ private void readRefreshTokens(JsonReader reader) throws IOException { private Map accessTokenToClientRefs = new HashMap<>(); private Map accessTokenToAuthHolderRefs = new HashMap<>(); private Map accessTokenToRefreshTokenRefs = new HashMap<>(); - private Map accessTokenToIdTokenRefs = new HashMap<>(); private Map accessTokenOldToNewIdMap = new HashMap<>(); /** @@ -243,7 +242,6 @@ private void readAccessTokens(JsonReader reader) throws IOException { String clientId = null; Long authHolderId = null; Long refreshTokenId = null; - Long idTokenId = null; while (reader.hasNext()) { switch (reader.peek()) { case END_OBJECT: @@ -271,8 +269,6 @@ private void readAccessTokens(JsonReader reader) throws IOException { authHolderId = reader.nextLong(); } else if (name.equals("refreshTokenId")) { refreshTokenId = reader.nextLong(); - } else if (name.equals("idTokenId")) { - idTokenId = reader.nextLong(); } else if (name.equals("scope")) { Set scope = readSet(reader); token.setScope(scope); @@ -296,9 +292,6 @@ private void readAccessTokens(JsonReader reader) throws IOException { if (refreshTokenId != null) { accessTokenToRefreshTokenRefs.put(currentId, refreshTokenId); } - if (idTokenId != null) { - accessTokenToIdTokenRefs.put(currentId, idTokenId); - } accessTokenOldToNewIdMap.put(currentId, newId); logger.debug("Read access token {}", currentId); } @@ -883,16 +876,6 @@ private void fixObjectReferences() { } accessTokenToRefreshTokenRefs.clear(); refreshTokenOldToNewIdMap.clear(); - for (Long oldAccessTokenId : accessTokenToIdTokenRefs.keySet()) { - Long oldIdTokenId = accessTokenToIdTokenRefs.get(oldAccessTokenId); - Long newIdTokenId = accessTokenOldToNewIdMap.get(oldIdTokenId); - OAuth2AccessTokenEntity idToken = tokenRepository.getAccessTokenById(newIdTokenId); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); - OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); - accessToken.setIdToken(idToken); - tokenRepository.saveAccessToken(accessToken); - } - accessTokenToIdTokenRefs.clear(); whitelistedSiteOldToNewIdMap.clear(); for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java index b4ca8f5140..7955ded756 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java @@ -226,7 +226,6 @@ private void readRefreshTokens(JsonReader reader) throws IOException { private Map accessTokenToClientRefs = new HashMap<>(); private Map accessTokenToAuthHolderRefs = new HashMap<>(); private Map accessTokenToRefreshTokenRefs = new HashMap<>(); - private Map accessTokenToIdTokenRefs = new HashMap<>(); private Map accessTokenOldToNewIdMap = new HashMap<>(); /** @@ -246,7 +245,6 @@ private void readAccessTokens(JsonReader reader) throws IOException { String clientId = null; Long authHolderId = null; Long refreshTokenId = null; - Long idTokenId = null; while (reader.hasNext()) { switch (reader.peek()) { case END_OBJECT: @@ -274,8 +272,6 @@ private void readAccessTokens(JsonReader reader) throws IOException { authHolderId = reader.nextLong(); } else if (name.equals("refreshTokenId")) { refreshTokenId = reader.nextLong(); - } else if (name.equals("idTokenId")) { - idTokenId = reader.nextLong(); } else if (name.equals("scope")) { Set scope = readSet(reader); token.setScope(scope); @@ -299,9 +295,6 @@ private void readAccessTokens(JsonReader reader) throws IOException { if (refreshTokenId != null) { accessTokenToRefreshTokenRefs.put(currentId, refreshTokenId); } - if (idTokenId != null) { - accessTokenToIdTokenRefs.put(currentId, idTokenId); - } accessTokenOldToNewIdMap.put(currentId, newId); logger.debug("Read access token {}", currentId); } @@ -897,16 +890,6 @@ private void fixObjectReferences() { } accessTokenToRefreshTokenRefs.clear(); refreshTokenOldToNewIdMap.clear(); - for (Long oldAccessTokenId : accessTokenToIdTokenRefs.keySet()) { - Long oldIdTokenId = accessTokenToIdTokenRefs.get(oldAccessTokenId); - Long newIdTokenId = accessTokenOldToNewIdMap.get(oldIdTokenId); - OAuth2AccessTokenEntity idToken = tokenRepository.getAccessTokenById(newIdTokenId); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); - OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); - accessToken.setIdToken(idToken); - tokenRepository.saveAccessToken(accessToken); - } - accessTokenToIdTokenRefs.clear(); for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java index 0f0a9d3500..da28b5d8f7 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java @@ -137,7 +137,6 @@ public class MITREidDataService_1_2 extends MITREidDataServiceSupport implements private static final String REQUEST_PARAMETERS = "requestParameters"; private static final String TYPE = "type"; private static final String SCOPE = "scope"; - private static final String ID_TOKEN_ID = "idTokenId"; private static final String REFRESH_TOKEN_ID = "refreshTokenId"; private static final String VALUE = "value"; private static final String AUTHENTICATION_HOLDER_ID = "authenticationHolderId"; @@ -291,7 +290,6 @@ private void readRefreshTokens(JsonReader reader) throws IOException { private Map accessTokenToClientRefs = new HashMap(); private Map accessTokenToAuthHolderRefs = new HashMap(); private Map accessTokenToRefreshTokenRefs = new HashMap(); - private Map accessTokenToIdTokenRefs = new HashMap(); private Map accessTokenOldToNewIdMap = new HashMap(); /** @@ -311,7 +309,6 @@ private void readAccessTokens(JsonReader reader) throws IOException { String clientId = null; Long authHolderId = null; Long refreshTokenId = null; - Long idTokenId = null; while (reader.hasNext()) { switch (reader.peek()) { case END_OBJECT: @@ -339,8 +336,6 @@ private void readAccessTokens(JsonReader reader) throws IOException { authHolderId = reader.nextLong(); } else if (name.equals(REFRESH_TOKEN_ID)) { refreshTokenId = reader.nextLong(); - } else if (name.equals(ID_TOKEN_ID)) { - idTokenId = reader.nextLong(); } else if (name.equals(SCOPE)) { Set scope = readSet(reader); token.setScope(scope); @@ -364,9 +359,6 @@ private void readAccessTokens(JsonReader reader) throws IOException { if (refreshTokenId != null) { accessTokenToRefreshTokenRefs.put(currentId, refreshTokenId); } - if (idTokenId != null) { - accessTokenToIdTokenRefs.put(currentId, idTokenId); - } accessTokenOldToNewIdMap.put(currentId, newId); logger.debug("Read access token {}", currentId); } @@ -888,16 +880,6 @@ private void fixObjectReferences() { } accessTokenToRefreshTokenRefs.clear(); refreshTokenOldToNewIdMap.clear(); - for (Long oldAccessTokenId : accessTokenToIdTokenRefs.keySet()) { - Long oldIdTokenId = accessTokenToIdTokenRefs.get(oldAccessTokenId); - Long newIdTokenId = accessTokenOldToNewIdMap.get(oldIdTokenId); - OAuth2AccessTokenEntity idToken = tokenRepository.getAccessTokenById(newIdTokenId); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); - OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); - accessToken.setIdToken(idToken); - tokenRepository.saveAccessToken(accessToken); - } - accessTokenToIdTokenRefs.clear(); for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java index 38e1fdf93c..ed0503110b 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java @@ -139,7 +139,6 @@ public class MITREidDataService_1_3 extends MITREidDataServiceSupport implements private static final String REQUEST_PARAMETERS = "requestParameters"; private static final String TYPE = "type"; private static final String SCOPE = "scope"; - private static final String ID_TOKEN_ID = "idTokenId"; private static final String REFRESH_TOKEN_ID = "refreshTokenId"; private static final String VALUE = "value"; private static final String AUTHENTICATION_HOLDER_ID = "authenticationHolderId"; @@ -257,8 +256,6 @@ private void writeAccessTokens(JsonWriter writer) throws IOException { .value((token.getAuthenticationHolder() != null) ? token.getAuthenticationHolder().getId() : null); writer.name(REFRESH_TOKEN_ID) .value((token.getRefreshToken() != null) ? token.getRefreshToken().getId() : null); - writer.name(ID_TOKEN_ID) - .value((token.getIdToken() != null) ? token.getIdToken().getId() : null); writer.name(SCOPE); writer.beginArray(); for (String s : token.getScope()) { @@ -658,7 +655,6 @@ private void readRefreshTokens(JsonReader reader) throws IOException { private Map accessTokenToClientRefs = new HashMap(); private Map accessTokenToAuthHolderRefs = new HashMap(); private Map accessTokenToRefreshTokenRefs = new HashMap(); - private Map accessTokenToIdTokenRefs = new HashMap(); private Map accessTokenOldToNewIdMap = new HashMap(); /** @@ -678,7 +674,6 @@ private void readAccessTokens(JsonReader reader) throws IOException { String clientId = null; Long authHolderId = null; Long refreshTokenId = null; - Long idTokenId = null; while (reader.hasNext()) { switch (reader.peek()) { case END_OBJECT: @@ -706,8 +701,6 @@ private void readAccessTokens(JsonReader reader) throws IOException { authHolderId = reader.nextLong(); } else if (name.equals(REFRESH_TOKEN_ID)) { refreshTokenId = reader.nextLong(); - } else if (name.equals(ID_TOKEN_ID)) { - idTokenId = reader.nextLong(); } else if (name.equals(SCOPE)) { Set scope = readSet(reader); token.setScope(scope); @@ -731,9 +724,6 @@ private void readAccessTokens(JsonReader reader) throws IOException { if (refreshTokenId != null) { accessTokenToRefreshTokenRefs.put(currentId, refreshTokenId); } - if (idTokenId != null) { - accessTokenToIdTokenRefs.put(currentId, idTokenId); - } accessTokenOldToNewIdMap.put(currentId, newId); logger.debug("Read access token {}", currentId); } @@ -1263,16 +1253,6 @@ private void fixObjectReferences() { } accessTokenToRefreshTokenRefs.clear(); refreshTokenOldToNewIdMap.clear(); - for (Long oldAccessTokenId : accessTokenToIdTokenRefs.keySet()) { - Long oldIdTokenId = accessTokenToIdTokenRefs.get(oldAccessTokenId); - Long newIdTokenId = accessTokenOldToNewIdMap.get(oldIdTokenId); - OAuth2AccessTokenEntity idToken = tokenRepository.getAccessTokenById(newIdTokenId); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); - OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); - accessToken.setIdToken(idToken); - tokenRepository.saveAccessToken(accessToken); - } - accessTokenToIdTokenRefs.clear(); for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectTokenEnhancer.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectTokenEnhancer.java index 6a9b277f0b..9719aa5fdc 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectTokenEnhancer.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectTokenEnhancer.java @@ -44,6 +44,7 @@ import com.google.common.collect.Lists; import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWT; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.JWTClaimsSet.Builder; import com.nimbusds.jwt.SignedJWT; @@ -132,12 +133,12 @@ public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentica if (userInfo != null) { - OAuth2AccessTokenEntity idTokenEntity = connectTokenService.createIdToken(client, + JWT idToken = connectTokenService.createIdToken(client, originalAuthRequest, claims.getIssueTime(), userInfo.getSub(), token); // attach the id token to the parent access token - token.setIdToken(idTokenEntity); + token.setIdToken(idToken); } else { // can't create an id token if we can't find the user logger.warn("Request for ID token when no user is present."); diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java index 6574791b31..fdb196f85f 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java @@ -295,7 +295,6 @@ public void testImportAccessTokens() throws IOException, ParseException { token2.setExpiration(expirationDate2); token2.setJwt(JWTParser.parse("eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3OTI5NjgsImF1ZCI6WyJjbGllbnQiXSwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwODBcL29wZW5pZC1jb25uZWN0LXNlcnZlci13ZWJhcHBcLyIsImp0aSI6IjBmZGE5ZmRiLTYyYzItNGIzZS05OTdiLWU0M2VhMDUwMzNiOSIsImlhdCI6MTQxMjc4OTM2OH0.xgaVpRLYE5MzbgXfE0tZt823tjAm6Oh3_kdR1P2I9jRLR6gnTlBQFlYi3Y_0pWNnZSerbAE8Tn6SJHZ9k-curVG0-ByKichV7CNvgsE5X_2wpEaUzejvKf8eZ-BammRY-ie6yxSkAarcUGMvGGOLbkFcz5CtrBpZhfd75J49BIQ")); token2.setAuthenticationHolder(mockedAuthHolder2); - token2.setIdToken(token1); token2.setRefreshToken(mockRefreshToken2); token2.setScope(ImmutableSet.of("openid", "offline_access", "email", "profile")); token2.setTokenType("Bearer"); @@ -365,8 +364,8 @@ public AuthenticationHolderEntity answer(InvocationOnMock invocation) throws Thr } }); dataService.importData(reader); - //2 times for token, 2 times to update client, 2 times to update authHolder, 2 times to update id token, 2 times to update refresh token - verify(tokenRepository, times(8)).saveAccessToken(capturedAccessTokens.capture()); + //2 times for token, 2 times to update client, 2 times to update authHolder, 1 times to update refresh token + verify(tokenRepository, times(7)).saveAccessToken(capturedAccessTokens.capture()); List savedAccessTokens = new ArrayList(fakeDb.values()); //capturedAccessTokens.getAllValues(); Collections.sort(savedAccessTokens, new accessTokenIdComparator()); diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java index 2e80d6a8f7..cb2f589048 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java @@ -299,7 +299,6 @@ public void testImportAccessTokens() throws IOException, ParseException { token2.setExpiration(expirationDate2); token2.setJwt(JWTParser.parse("eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3OTI5NjgsImF1ZCI6WyJjbGllbnQiXSwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwODBcL29wZW5pZC1jb25uZWN0LXNlcnZlci13ZWJhcHBcLyIsImp0aSI6IjBmZGE5ZmRiLTYyYzItNGIzZS05OTdiLWU0M2VhMDUwMzNiOSIsImlhdCI6MTQxMjc4OTM2OH0.xgaVpRLYE5MzbgXfE0tZt823tjAm6Oh3_kdR1P2I9jRLR6gnTlBQFlYi3Y_0pWNnZSerbAE8Tn6SJHZ9k-curVG0-ByKichV7CNvgsE5X_2wpEaUzejvKf8eZ-BammRY-ie6yxSkAarcUGMvGGOLbkFcz5CtrBpZhfd75J49BIQ")); token2.setAuthenticationHolder(mockedAuthHolder2); - token2.setIdToken(token1); token2.setRefreshToken(mockRefreshToken2); token2.setScope(ImmutableSet.of("openid", "offline_access", "email", "profile")); token2.setTokenType("Bearer"); @@ -369,8 +368,8 @@ public AuthenticationHolderEntity answer(InvocationOnMock invocation) throws Thr } }); dataService.importData(reader); - //2 times for token, 2 times to update client, 2 times to update authHolder, 2 times to update id token, 2 times to update refresh token - verify(tokenRepository, times(8)).saveAccessToken(capturedAccessTokens.capture()); + //2 times for token, 2 times to update client, 2 times to update authHolder, 1 times to update refresh token + verify(tokenRepository, times(7)).saveAccessToken(capturedAccessTokens.capture()); List savedAccessTokens = new ArrayList(fakeDb.values()); //capturedAccessTokens.getAllValues(); Collections.sort(savedAccessTokens, new accessTokenIdComparator()); diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java index e12035fe25..44264eb33f 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java @@ -314,7 +314,6 @@ public void testImportAccessTokens() throws IOException, ParseException { token2.setExpiration(expirationDate2); token2.setJwt(JWTParser.parse("eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3OTI5NjgsImF1ZCI6WyJjbGllbnQiXSwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwODBcL29wZW5pZC1jb25uZWN0LXNlcnZlci13ZWJhcHBcLyIsImp0aSI6IjBmZGE5ZmRiLTYyYzItNGIzZS05OTdiLWU0M2VhMDUwMzNiOSIsImlhdCI6MTQxMjc4OTM2OH0.xgaVpRLYE5MzbgXfE0tZt823tjAm6Oh3_kdR1P2I9jRLR6gnTlBQFlYi3Y_0pWNnZSerbAE8Tn6SJHZ9k-curVG0-ByKichV7CNvgsE5X_2wpEaUzejvKf8eZ-BammRY-ie6yxSkAarcUGMvGGOLbkFcz5CtrBpZhfd75J49BIQ")); token2.setAuthenticationHolder(mockedAuthHolder2); - token2.setIdToken(token1); token2.setRefreshToken(mockRefreshToken2); token2.setScope(ImmutableSet.of("openid", "offline_access", "email", "profile")); token2.setTokenType("Bearer"); @@ -384,8 +383,8 @@ public AuthenticationHolderEntity answer(InvocationOnMock invocation) throws Thr } }); dataService.importData(reader); - //2 times for token, 2 times to update client, 2 times to update authHolder, 2 times to update id token, 2 times to update refresh token - verify(tokenRepository, times(8)).saveAccessToken(capturedAccessTokens.capture()); + //2 times for token, 2 times to update client, 2 times to update authHolder, 1 times to update refresh token + verify(tokenRepository, times(7)).saveAccessToken(capturedAccessTokens.capture()); List savedAccessTokens = new ArrayList(fakeDb.values()); //capturedAccessTokens.getAllValues(); Collections.sort(savedAccessTokens, new accessTokenIdComparator()); diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java index 71fccf3bbf..4d2e71b720 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java @@ -423,7 +423,6 @@ public void testExportAccessTokens() throws IOException, ParseException { token2.setExpiration(expirationDate2); token2.setJwt(JWTParser.parse("eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3OTI5NjgsImF1ZCI6WyJjbGllbnQiXSwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwODBcL29wZW5pZC1jb25uZWN0LXNlcnZlci13ZWJhcHBcLyIsImp0aSI6IjBmZGE5ZmRiLTYyYzItNGIzZS05OTdiLWU0M2VhMDUwMzNiOSIsImlhdCI6MTQxMjc4OTM2OH0.xgaVpRLYE5MzbgXfE0tZt823tjAm6Oh3_kdR1P2I9jRLR6gnTlBQFlYi3Y_0pWNnZSerbAE8Tn6SJHZ9k-curVG0-ByKichV7CNvgsE5X_2wpEaUzejvKf8eZ-BammRY-ie6yxSkAarcUGMvGGOLbkFcz5CtrBpZhfd75J49BIQ")); token2.setAuthenticationHolder(mockedAuthHolder2); - token2.setIdToken(token1); token2.setRefreshToken(mockRefreshToken2); token2.setScope(ImmutableSet.of("openid", "offline_access", "email", "profile")); token2.setTokenType("Bearer"); @@ -505,13 +504,8 @@ public void testExportAccessTokens() throws IOException, ParseException { assertThat(token.get("authenticationHolderId").getAsLong(), equalTo(compare.getAuthenticationHolder().getId())); assertTrue(token.get("scope").isJsonArray()); assertThat(jsonArrayToStringSet(token.getAsJsonArray("scope")), equalTo(compare.getScope())); - if(token.get("idTokenId").isJsonNull()) { - assertNull(compare.getIdToken()); - } else { - assertThat(token.get("idTokenId").getAsLong(), equalTo(compare.getIdToken().getId())); - } if(token.get("refreshTokenId").isJsonNull()) { - assertNull(compare.getIdToken()); + assertNull(compare.getRefreshToken()); } else { assertThat(token.get("refreshTokenId").getAsLong(), equalTo(compare.getRefreshToken().getId())); } @@ -567,7 +561,6 @@ public void testImportAccessTokens() throws IOException, ParseException { token2.setExpiration(expirationDate2); token2.setJwt(JWTParser.parse("eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3OTI5NjgsImF1ZCI6WyJjbGllbnQiXSwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwODBcL29wZW5pZC1jb25uZWN0LXNlcnZlci13ZWJhcHBcLyIsImp0aSI6IjBmZGE5ZmRiLTYyYzItNGIzZS05OTdiLWU0M2VhMDUwMzNiOSIsImlhdCI6MTQxMjc4OTM2OH0.xgaVpRLYE5MzbgXfE0tZt823tjAm6Oh3_kdR1P2I9jRLR6gnTlBQFlYi3Y_0pWNnZSerbAE8Tn6SJHZ9k-curVG0-ByKichV7CNvgsE5X_2wpEaUzejvKf8eZ-BammRY-ie6yxSkAarcUGMvGGOLbkFcz5CtrBpZhfd75J49BIQ")); token2.setAuthenticationHolder(mockedAuthHolder2); - token2.setIdToken(token1); token2.setRefreshToken(mockRefreshToken2); token2.setScope(ImmutableSet.of("openid", "offline_access", "email", "profile")); token2.setTokenType("Bearer"); @@ -637,8 +630,8 @@ public AuthenticationHolderEntity answer(InvocationOnMock invocation) throws Thr } }); dataService.importData(reader); - //2 times for token, 2 times to update client, 2 times to update authHolder, 2 times to update id token, 2 times to update refresh token - verify(tokenRepository, times(8)).saveAccessToken(capturedAccessTokens.capture()); + //2 times for token, 2 times to update client, 2 times to update authHolder, 1 times to update refresh token + verify(tokenRepository, times(7)).saveAccessToken(capturedAccessTokens.capture()); List savedAccessTokens = new ArrayList(fakeDb.values()); //capturedAccessTokens.getAllValues(); Collections.sort(savedAccessTokens, new accessTokenIdComparator()); diff --git a/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java b/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java index 73ddddecbb..01cdb2f743 100644 --- a/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java +++ b/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java @@ -442,8 +442,10 @@ private void writeAccessTokens(JsonWriter writer) throws IOException { .value((token.getAuthenticationHolder() != null) ? token.getAuthenticationHolder().getId() : null); writer.name(REFRESH_TOKEN_ID) .value((token.getRefreshToken() != null) ? token.getRefreshToken().getId() : null); +/* writer.name(ID_TOKEN_ID) .value((token.getIdToken() != null) ? token.getIdToken().getId() : null); +*/ writer.name(SCOPE); writer.beginArray(); for (String s : token.getScope()) { @@ -1780,16 +1782,6 @@ private void fixObjectReferences() { } accessTokenToRefreshTokenRefs.clear(); refreshTokenOldToNewIdMap.clear(); - for (Long oldAccessTokenId : accessTokenToIdTokenRefs.keySet()) { - Long oldIdTokenId = accessTokenToIdTokenRefs.get(oldAccessTokenId); - Long newIdTokenId = accessTokenOldToNewIdMap.get(oldIdTokenId); - OAuth2AccessTokenEntity idToken = tokenRepository.getAccessTokenById(newIdTokenId); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); - OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); - accessToken.setIdToken(idToken); - tokenRepository.saveAccessToken(accessToken); - } - accessTokenToIdTokenRefs.clear(); for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); From 63bd8d18fb547936675c64ee7f7770c8222f15b7 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 21 Dec 2016 13:44:40 -0500 Subject: [PATCH 090/269] cleaned ID token views from UI --- .../src/main/webapp/resources/js/token.js | 1 - .../src/main/webapp/resources/template/token.html | 5 ----- 2 files changed, 6 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/token.js b/openid-connect-server-webapp/src/main/webapp/resources/js/token.js index 58a38c3d27..3df1c49b83 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/token.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/token.js @@ -21,7 +21,6 @@ var AccessTokenModel = Backbone.Model.extend({ defaults:{ id:null, value:null, - idTokenId:null, refreshTokenId:null, scopes:[], clientId:null, diff --git a/openid-connect-server-webapp/src/main/webapp/resources/template/token.html b/openid-connect-server-webapp/src/main/webapp/resources/template/token.html index 4ddc858f4e..1bd2afc1e4 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/template/token.html +++ b/openid-connect-server-webapp/src/main/webapp/resources/template/token.html @@ -98,11 +98,6 @@
+ Refresh <% } %> - <% if (token.idTokenId != null) { %> -
- + ID - <% } %> - From bea3af2470a32395248ff58078ddc327cc922da2 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 21 Dec 2016 13:44:49 -0500 Subject: [PATCH 091/269] database table sync --- .../src/main/resources/db/mysql/mysql_database_tables.sql | 1 - .../src/main/resources/db/oracle/oracle_database_tables.sql | 1 - .../src/main/resources/db/psql/psql_database_tables.sql | 1 - 3 files changed, 3 deletions(-) diff --git a/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql index fea38cfc2f..17554e68f7 100644 --- a/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql @@ -10,7 +10,6 @@ CREATE TABLE IF NOT EXISTS access_token ( refresh_token_id BIGINT, client_id BIGINT, auth_holder_id BIGINT, - id_token_id BIGINT, approved_site_id BIGINT ); diff --git a/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql index 5440f32681..a8d45bf476 100644 --- a/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql @@ -10,7 +10,6 @@ CREATE TABLE access_token ( refresh_token_id NUMBER(19), client_id NUMBER(19), auth_holder_id NUMBER(19), - id_token_id NUMBER(19), approved_site_id NUMBER(19) ); CREATE SEQUENCE access_token_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; diff --git a/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql index 01682b7053..8df6b4f8ea 100644 --- a/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql @@ -10,7 +10,6 @@ CREATE TABLE IF NOT EXISTS access_token ( refresh_token_id BIGINT, client_id BIGINT, auth_holder_id BIGINT, - id_token_id BIGINT, approved_site_id BIGINT ); From 275c1b7e1cd0912f3c0201ca7782af2818b6b88a Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 21 Dec 2016 14:48:37 -0500 Subject: [PATCH 092/269] user info fetcher cache throws error instead of returning null (as per library contract), closes #1144 --- .../connect/client/UserInfoFetcher.java | 87 +++++++++---------- 1 file changed, 41 insertions(+), 46 deletions(-) diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java index f1142d970c..1de305b724 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.net.URI; +import java.net.URISyntaxException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -89,7 +90,7 @@ private class UserInfoLoader extends CacheLoader form = new LinkedMultiValueMap<>(); - form.add("access_token", token.getAccessTokenValue()); - - RestTemplate restTemplate = new RestTemplate(factory); - userInfoString = restTemplate.postForObject(serverConfiguration.getUserInfoUri(), form, String.class); - } else if (serverConfiguration.getUserInfoTokenMethod().equals(UserInfoTokenMethod.QUERY)) { - URIBuilder builder = new URIBuilder(serverConfiguration.getUserInfoUri()); - builder.setParameter("access_token", token.getAccessTokenValue()); - - RestTemplate restTemplate = new RestTemplate(factory); - userInfoString = restTemplate.getForObject(builder.toString(), String.class); - } - - - if (!Strings.isNullOrEmpty(userInfoString)) { - - JsonObject userInfoJson = new JsonParser().parse(userInfoString).getAsJsonObject(); - - UserInfo userInfo = fromJson(userInfoJson); - - return userInfo; - } else { - // didn't get anything, return null - return null; - } - } catch (Exception e) { - logger.warn("Error fetching userinfo", e); - return null; + if (serverConfiguration.getUserInfoTokenMethod() == null || serverConfiguration.getUserInfoTokenMethod().equals(UserInfoTokenMethod.HEADER)) { + RestTemplate restTemplate = new RestTemplate(factory) { + + @Override + protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException { + ClientHttpRequest httpRequest = super.createRequest(url, method); + httpRequest.getHeaders().add("Authorization", String.format("Bearer %s", token.getAccessTokenValue())); + return httpRequest; + } + }; + + userInfoString = restTemplate.getForObject(serverConfiguration.getUserInfoUri(), String.class); + + } else if (serverConfiguration.getUserInfoTokenMethod().equals(UserInfoTokenMethod.FORM)) { + MultiValueMap form = new LinkedMultiValueMap<>(); + form.add("access_token", token.getAccessTokenValue()); + + RestTemplate restTemplate = new RestTemplate(factory); + userInfoString = restTemplate.postForObject(serverConfiguration.getUserInfoUri(), form, String.class); + } else if (serverConfiguration.getUserInfoTokenMethod().equals(UserInfoTokenMethod.QUERY)) { + URIBuilder builder = new URIBuilder(serverConfiguration.getUserInfoUri()); + builder.setParameter("access_token", token.getAccessTokenValue()); + + RestTemplate restTemplate = new RestTemplate(factory); + userInfoString = restTemplate.getForObject(builder.toString(), String.class); + } + + + if (!Strings.isNullOrEmpty(userInfoString)) { + + JsonObject userInfoJson = new JsonParser().parse(userInfoString).getAsJsonObject(); + + UserInfo userInfo = fromJson(userInfoJson); + + return userInfo; + } else { + // didn't get anything throw exception + throw new IllegalArgumentException("Unable to load user info"); } } From 0e703ef9f98fda34d21d3c5cae1eccfbcb81a73d Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 21 Dec 2016 15:50:24 -0500 Subject: [PATCH 093/269] update a few dependency versions, closes #1145 --- openid-connect-server/pom.xml | 4 ++++ pom.xml | 13 +++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/openid-connect-server/pom.xml b/openid-connect-server/pom.xml index 5033d09e68..ca27ac73c7 100644 --- a/openid-connect-server/pom.xml +++ b/openid-connect-server/pom.xml @@ -51,6 +51,10 @@ org.eclipse.persistence.core
+ + org.apache.commons + commons-io + OpenID Connect server libraries for Spring and Spring Security. diff --git a/pom.xml b/pom.xml index c5b164a037..4225cc6904 100644 --- a/pom.xml +++ b/pom.xml @@ -521,17 +521,17 @@ com.google.guava guava - 18.0 + 20.0 com.google.code.gson gson - 2.3.1 + 2.8.0 org.apache.httpcomponents httpclient - 4.3.6 + 4.5.2 commons-logging @@ -542,7 +542,7 @@ com.nimbusds nimbus-jose-jwt - 4.3 + 4.33 org.bouncycastle @@ -554,6 +554,11 @@ org.eclipse.persistence.core 2.5.1 + + org.apache.commons + commons-io + 1.3.2 + From d5e8dd31a8f48560151279f655bb9d47ef971c8f Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 22 Dec 2016 14:30:59 -0500 Subject: [PATCH 094/269] update to Spring Security 4 --- .../src/main/webapp/WEB-INF/application-context.xml | 2 +- .../src/main/webapp/WEB-INF/assertion-config.xml | 2 +- .../src/main/webapp/WEB-INF/authz-config.xml | 2 +- .../src/main/webapp/WEB-INF/crypto-config.xml | 2 +- .../src/main/webapp/WEB-INF/jpa-config.xml | 2 +- .../src/main/webapp/WEB-INF/local-config.xml | 2 +- .../src/main/webapp/WEB-INF/server-config.xml | 2 +- .../src/main/webapp/WEB-INF/spring-servlet.xml | 2 +- .../src/main/webapp/WEB-INF/user-context.xml | 8 +++++--- .../src/main/webapp/WEB-INF/views/login.jsp | 6 +++--- pom.xml | 12 +++++------- 11 files changed, 21 insertions(+), 21 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml index 6a7e0be9fe..58253848bb 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml @@ -25,7 +25,7 @@ xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/assertion-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/assertion-config.xml index 3a7c4fb47b..653e117b1e 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/assertion-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/assertion-config.xml @@ -24,7 +24,7 @@ xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml index b220267f57..6776e9ade6 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml @@ -24,7 +24,7 @@ xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/crypto-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/crypto-config.xml index 0301caa2f7..840c514091 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/crypto-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/crypto-config.xml @@ -24,7 +24,7 @@ xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml index 93400558bf..b95cbd9fdd 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml @@ -24,7 +24,7 @@ xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/local-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/local-config.xml index c980446e2b..1624dc8918 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/local-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/local-config.xml @@ -24,7 +24,7 @@ xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/server-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/server-config.xml index 2302b19b45..cba200fbe6 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/server-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/server-config.xml @@ -24,7 +24,7 @@ xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/spring-servlet.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/spring-servlet.xml index b3467051ce..0e4aa46232 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/spring-servlet.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/spring-servlet.xml @@ -24,7 +24,7 @@ xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml index b45ac48aa2..1251f765d9 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml @@ -24,7 +24,7 @@ xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> @@ -37,10 +37,12 @@ - - + + + + diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/login.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/login.jsp index f46abf3a7e..fb71cab2a2 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/login.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/login.jsp @@ -27,17 +27,17 @@ $(document).ready(function() {
-
+
- " autocorrect="off" autocapitalize="off" autocomplete="off" spellcheck="false" value="" id="j_username" name="j_username"> + " autocorrect="off" autocapitalize="off" autocomplete="off" spellcheck="false" value="" id="j_username" name="username">
- " autocorrect="off" autocapitalize="off" autocomplete="off" spellcheck="false" id="j_password" name="j_password"> + " autocorrect="off" autocapitalize="off" autocomplete="off" spellcheck="false" id="j_password" name="password">
diff --git a/pom.xml b/pom.xml index 4225cc6904..5a167cb095 100644 --- a/pom.xml +++ b/pom.xml @@ -71,8 +71,6 @@ 1.8 - 4.1.1.RELEASE - 3.2.5.RELEASE 1.7.21 A reference implementation of OpenID Connect (http://openid.net/connect/), OAuth 2.0, and UMA built on top of Java, Spring, and Spring Security. The project contains a fully functioning server, client, and utility library. @@ -314,7 +312,7 @@ org.springframework spring-framework-bom - 4.1.9.RELEASE + 4.3.4.RELEASE pom import @@ -335,14 +333,14 @@ org.springframework.security spring-security-bom - 3.2.9.RELEASE + 4.2.0.RELEASE pom import org.springframework.security.oauth spring-security-oauth2 - 2.0.3.RELEASE + 2.0.12.RELEASE @@ -373,7 +371,7 @@ org.hsqldb hsqldb - 2.2.9 + 2.3.4 org.postgresql @@ -393,7 +391,7 @@ org.eclipse.persistence javax.persistence - 2.1.0 + 2.1.1 com.zaxxer From fc91043807455d75cf9549a7d0f1e5ccd8af25f9 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 22 Dec 2016 15:17:18 -0500 Subject: [PATCH 095/269] using defaults from spring 4 --- .../src/main/webapp/WEB-INF/user-context.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml index 1251f765d9..20ea70665a 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml @@ -37,7 +37,7 @@ - + From 71bb8d643996151d8220463af2b61c1b5d132569 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 27 Dec 2016 21:04:38 -0500 Subject: [PATCH 096/269] Spring 4.3 namespace --- .../src/main/webapp/WEB-INF/application-context.xml | 10 +++++----- .../src/main/webapp/WEB-INF/assertion-config.xml | 8 ++++---- .../src/main/webapp/WEB-INF/authz-config.xml | 8 ++++---- .../src/main/webapp/WEB-INF/crypto-config.xml | 8 ++++---- .../src/main/webapp/WEB-INF/data-context.xml | 4 ++-- .../src/main/webapp/WEB-INF/jpa-config.xml | 8 ++++---- .../src/main/webapp/WEB-INF/local-config.xml | 8 ++++---- .../src/main/webapp/WEB-INF/server-config.xml | 8 ++++---- .../src/main/webapp/WEB-INF/spring-servlet.xml | 8 ++++---- .../src/main/webapp/WEB-INF/task-config.xml | 4 ++-- .../src/main/webapp/WEB-INF/user-context.xml | 8 ++++---- .../src/main/webapp/WEB-INF/application-context.xml | 10 +++++----- .../src/main/webapp/WEB-INF/server-config.xml | 8 ++++---- .../src/main/webapp/WEB-INF/user-context.xml | 8 ++++---- 14 files changed, 54 insertions(+), 54 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml index 58253848bb..108c209191 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml @@ -24,12 +24,12 @@ xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd - http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd + http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/assertion-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/assertion-config.xml index 653e117b1e..370cb8af08 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/assertion-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/assertion-config.xml @@ -23,11 +23,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml index 6776e9ade6..9c72fbbc5e 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml @@ -23,11 +23,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml index 8143fdc81d..1649fc40dc 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml @@ -18,8 +18,8 @@ + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd + http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml index b95cbd9fdd..d7ce5b6f09 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml @@ -23,11 +23,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/local-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/local-config.xml index 1624dc8918..dd22743568 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/local-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/local-config.xml @@ -23,11 +23,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/server-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/server-config.xml index cba200fbe6..2c9bdccffb 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/server-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/server-config.xml @@ -23,11 +23,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/spring-servlet.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/spring-servlet.xml index 0e4aa46232..12423268a9 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/spring-servlet.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/spring-servlet.xml @@ -23,11 +23,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/task-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/task-config.xml index 16fb2eee94..cf51979559 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/task-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/task-config.xml @@ -18,8 +18,8 @@ + xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.3.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml index 20ea70665a..c6ac4ed9dd 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml @@ -23,11 +23,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> diff --git a/uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml b/uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml index da03d5bf85..ea0d0fa031 100644 --- a/uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml +++ b/uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml @@ -24,12 +24,12 @@ xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd - http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd + http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> diff --git a/uma-server-webapp/src/main/webapp/WEB-INF/server-config.xml b/uma-server-webapp/src/main/webapp/WEB-INF/server-config.xml index 01642dc4f7..df1fe557ff 100644 --- a/uma-server-webapp/src/main/webapp/WEB-INF/server-config.xml +++ b/uma-server-webapp/src/main/webapp/WEB-INF/server-config.xml @@ -23,11 +23,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> diff --git a/uma-server-webapp/src/main/webapp/WEB-INF/user-context.xml b/uma-server-webapp/src/main/webapp/WEB-INF/user-context.xml index 8ca95c075a..ce4e945afe 100644 --- a/uma-server-webapp/src/main/webapp/WEB-INF/user-context.xml +++ b/uma-server-webapp/src/main/webapp/WEB-INF/user-context.xml @@ -23,11 +23,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> From 0d162b1423269685adbd1d77cbf6c0c90a62cd14 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 5 Jan 2017 17:37:22 -0500 Subject: [PATCH 097/269] disabled CSRF on API endpoints, changed auth manager to a bean ID instead of reference, changed filter order to keep SECOAUTH from clearing out security contexts. --- .../webapp/WEB-INF/application-context.xml | 22 ++++++++++++++----- .../src/main/webapp/WEB-INF/user-context.xml | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml index 108c209191..ea7c02cac8 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml @@ -86,48 +86,56 @@ + + + + - + + - + + - + + - + + - + + - + + diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml index c6ac4ed9dd..5de925f153 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml @@ -29,7 +29,7 @@ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> - + From 9b715f9632d5555fd628ff8b27e13728ba4354ff Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 5 Jan 2017 17:38:03 -0500 Subject: [PATCH 098/269] put error logging inside the curried callback --- .../src/main/webapp/resources/js/admin.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js b/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js index 48e7e09deb..766ca405e9 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js @@ -375,14 +375,14 @@ var ErrorHandlerView = Backbone.View.extend({ message = {}; } - if (message.log) { - console.log(message.log); - } - var _self = this; return function(model, response, options) { + if (message.log) { + console.log(message.log); + } + _self.showErrorMessage( _self.headerTemplate({message: message, model: model, response: response, options: options}), _self.template({message: message, model: model, response: response, options: options}) From d862ba9fd8224f8413cb7d074bb171c88c239efa Mon Sep 17 00:00:00 2001 From: mialtamimi Date: Sun, 1 Jan 2017 12:59:01 +0300 Subject: [PATCH 099/269] add indexes on certain fields to shorten time taken to issue a new access token --- .../main/resources/db/hsql/hsql_database_tables.sql | 11 ++++++++++- .../main/resources/db/mysql/mysql_database_tables.sql | 9 +++++++++ .../resources/db/oracle/oracle_database_tables.sql | 9 +++++++++ .../main/resources/db/psql/psql_database_tables.sql | 11 ++++++++++- 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql index 50108c4db7..90bea3092c 100644 --- a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql @@ -365,4 +365,13 @@ CREATE INDEX at_tv_idx ON access_token(token_value); CREATE INDEX ts_oi_idx ON token_scope(owner_id); CREATE INDEX at_exp_idx ON access_token(expiration); CREATE INDEX rf_ahi_idx ON refresh_token(auth_holder_id); -CREATE INDEX cd_ci_idx ON client_details(client_id); \ No newline at end of file +CREATE INDEX cd_ci_idx ON client_details(client_id); +CREATE INDEX at_ahi_idx ON access_token(auth_holder_id); +CREATE INDEX aha_oi_idx ON authentication_holder_authority(owner_id); +CREATE INDEX ahe_oi_idx ON authentication_holder_extension(owner_id); +CREATE INDEX ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); +CREATE INDEX ahri_oi_idx ON authentication_holder_resource_id(owner_id); +CREATE INDEX ahrt_oi_idx ON authentication_holder_response_type(owner_id); +CREATE INDEX ahs_oi_idx ON authentication_holder_scope(owner_id); +CREATE INDEX ac_ahi_idx ON authorization_code(auth_holder_id); +CREATE INDEX suaa_oi_idx ON saved_user_auth_authority(owner_id); diff --git a/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql index 17554e68f7..04839343d5 100644 --- a/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql @@ -366,3 +366,12 @@ CREATE INDEX ts_oi_idx ON token_scope(owner_id); CREATE INDEX at_exp_idx ON access_token(expiration); CREATE INDEX rf_ahi_idx ON refresh_token(auth_holder_id); CREATE INDEX cd_ci_idx ON client_details(client_id); +CREATE INDEX at_ahi_idx ON access_token(auth_holder_id); +CREATE INDEX aha_oi_idx ON authentication_holder_authority(owner_id); +CREATE INDEX ahe_oi_idx ON authentication_holder_extension(owner_id); +CREATE INDEX ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); +CREATE INDEX ahri_oi_idx ON authentication_holder_resource_id(owner_id); +CREATE INDEX ahrt_oi_idx ON authentication_holder_response_type(owner_id); +CREATE INDEX ahs_oi_idx ON authentication_holder_scope(owner_id); +CREATE INDEX ac_ahi_idx ON authorization_code(auth_holder_id); +CREATE INDEX suaa_oi_idx ON saved_user_auth_authority(owner_id); diff --git a/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql index a8d45bf476..440a3a08ab 100644 --- a/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql @@ -399,3 +399,12 @@ CREATE INDEX at_tv_idx ON access_token(token_value); CREATE INDEX ts_oi_idx ON token_scope(owner_id); CREATE INDEX at_exp_idx ON access_token(expiration); CREATE INDEX rf_ahi_idx ON refresh_token(auth_holder_id); +CREATE INDEX at_ahi_idx ON access_token(auth_holder_id); +CREATE INDEX aha_oi_idx ON authentication_holder_authority(owner_id); +CREATE INDEX ahe_oi_idx ON authentication_holder_extension(owner_id); +CREATE INDEX ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); +CREATE INDEX ahri_oi_idx ON authentication_holder_resource_id(owner_id); +CREATE INDEX ahrt_oi_idx ON authentication_holder_response_type(owner_id); +CREATE INDEX ahs_oi_idx ON authentication_holder_scope(owner_id); +CREATE INDEX ac_ahi_idx ON authorization_code(auth_holder_id); +CREATE INDEX suaa_oi_idx ON saved_user_auth_authority(owner_id); diff --git a/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql index 8df6b4f8ea..47985f0ad3 100644 --- a/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql @@ -365,4 +365,13 @@ CREATE INDEX at_tv_idx ON access_token(token_value); CREATE INDEX ts_oi_idx ON token_scope(owner_id); CREATE INDEX at_exp_idx ON access_token(expiration); CREATE INDEX rf_ahi_idx ON refresh_token(auth_holder_id); -CREATE INDEX cd_ci_idx ON client_details(client_id); \ No newline at end of file +CREATE INDEX cd_ci_idx ON client_details(client_id); +CREATE INDEX at_ahi_idx ON access_token(auth_holder_id); +CREATE INDEX aha_oi_idx ON authentication_holder_authority(owner_id); +CREATE INDEX ahe_oi_idx ON authentication_holder_extension(owner_id); +CREATE INDEX ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); +CREATE INDEX ahri_oi_idx ON authentication_holder_resource_id(owner_id); +CREATE INDEX ahrt_oi_idx ON authentication_holder_response_type(owner_id); +CREATE INDEX ahs_oi_idx ON authentication_holder_scope(owner_id); +CREATE INDEX ac_ahi_idx ON authorization_code(auth_holder_id); +CREATE INDEX suaa_oi_idx ON saved_user_auth_authority(owner_id); From 0dd7eef4289a7682404412945f39008283257cc5 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 17 Jan 2017 14:38:51 -0500 Subject: [PATCH 100/269] Updated database examples, closes #1167 --- .../src/main/webapp/WEB-INF/data-context.xml | 69 ++++++++++--------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml index 1649fc40dc..e6e65d6c2b 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml @@ -30,7 +30,7 @@ + If you are using a file based HSQLDB you should not run this every time. --> @@ -49,26 +49,28 @@ - + + + + + - - - - - - - - + + + + - - - - --> + + + + + + + + + @@ -80,20 +82,23 @@ - - - - - - - - --> + + + + + + + + + + @@ -104,6 +109,11 @@ --> + + - - From 5993c879a62388befcc24478f74e09a0c2d8d847 Mon Sep 17 00:00:00 2001 From: Sebastian Schuster Date: Thu, 12 Jan 2017 13:17:52 +0100 Subject: [PATCH 101/269] Removed version from created war and added maven jetty plugin with context so URLs are prefixed correctly in uma-server-webapp --- uma-server-webapp/pom.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/uma-server-webapp/pom.xml b/uma-server-webapp/pom.xml index 9a82d26121..a934f6eb12 100644 --- a/uma-server-webapp/pom.xml +++ b/uma-server-webapp/pom.xml @@ -53,6 +53,7 @@ org.apache.maven.plugins maven-war-plugin + uma-server-webapp org.mitre @@ -61,6 +62,16 @@ + + org.eclipse.jetty + jetty-maven-plugin + + ${project.build.directory}/uma-server-webapp.war + + /uma-server-webapp + + + From 0c821fcb517d3be08b9b5306ebf0f89d2dc52ad6 Mon Sep 17 00:00:00 2001 From: Sebastian Schuster Date: Thu, 12 Jan 2017 13:21:33 +0100 Subject: [PATCH 102/269] Adapted overlayed application-context.xml in uma-server-webapp to base file --- .../src/main/webapp/WEB-INF/application-context.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml b/uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml index ea0d0fa031..7de114acb1 100644 --- a/uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml +++ b/uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml @@ -216,14 +216,14 @@ - - - - + + + + From 99d1b0cfecc4e889d550b90ee0c84164d448d103 Mon Sep 17 00:00:00 2001 From: Sebastian Schuster Date: Thu, 12 Jan 2017 13:27:30 +0100 Subject: [PATCH 103/269] Adapted changes from base admin.js to overlayed admin.js in uma-server-webapp --- .../src/main/webapp/resources/js/admin.js | 65 ++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/uma-server-webapp/src/main/webapp/resources/js/admin.js b/uma-server-webapp/src/main/webapp/resources/js/admin.js index 1eb6848d89..6f09706e50 100644 --- a/uma-server-webapp/src/main/webapp/resources/js/admin.js +++ b/uma-server-webapp/src/main/webapp/resources/js/admin.js @@ -366,6 +366,66 @@ var UserProfileView = Backbone.View.extend({ } }); +// error handler +var ErrorHandlerView = Backbone.View.extend({ + + initialize:function(options) { + this.options = options; + if (!this.template) { + this.template = _.template($('#tmpl-error-box').html()); + } + if (!this.headerTemplate) { + this.headerTemplate = _.template($('#tmpl-error-header').html()); + } + }, + + reloadPage:function(event) { + event.preventDefault(); + window.location.reload(true); + }, + + handleError:function(message) { + + if (!message) { + message = {}; + } + + if (message.log) { + console.log(message.log); + } + + var _self = this; + + return function(model, response, options) { + + _self.showErrorMessage( + _self.headerTemplate({message: message, model: model, response: response, options: options}), + _self.template({message: message, model: model, response: response, options: options}) + ); + + $('#modalAlert .modal-body .page-reload').on('click', _self.reloadPage); + + } + }, + + showErrorMessage:function(header, message) { + // hide the sheet if it's visible + $('#loadingbox').sheet('hide'); + + $('#modalAlert').i18n(); + $('#modalAlert div.modal-header').html(header); + $('#modalAlert .modal-body').html(message); + + $('#modalAlert').modal({ + 'backdrop': 'static', + 'keyboard': true, + 'show': true + }); + + } +}); + + // Router var AppRouter = Backbone.Router.extend({ @@ -431,6 +491,8 @@ var AppRouter = Backbone.Router.extend({ this.breadCrumbView.render(); + this.errorHandlerView = new ErrorHandlerView(); + var base = $('base').attr('href'); $.getJSON(base + '.well-known/openid-configuration', function(data) { app.serverConfiguration = data; @@ -1142,9 +1204,10 @@ $(function () { }); window.onerror = function ( message, filename, lineno, colno, error ){ + console.log(message); //Display an alert with an error message $('#modalAlert div.modal-header').html($.t('error.title')); - $('#modalAlert div.modal-body').html($.t('error.message') + '
' [filename, lineno, colno, error]); + $('#modalAlert div.modal-body').html($.t('error.message') + message + '
' + [filename, lineno, colno, error]); $("#modalAlert").modal({ // wire up the actual modal functionality and show the dialog "backdrop" : "static", From 93deef952f7ff5f4515dec90196548d0cd7d3514 Mon Sep 17 00:00:00 2001 From: Sebastian Schuster Date: Thu, 12 Jan 2017 18:00:36 +0100 Subject: [PATCH 104/269] Adapted uma-webapp-server database initialization scripts to base changes --- .../main/resources/db/{ => hsql}/clients.sql | 2 +- .../main/resources/db/{ => hsql}/scopes.sql | 2 +- .../src/main/resources/db/mysql/clients.sql | 69 +++++++++++++++++ .../src/main/resources/db/mysql/scopes.sql | 33 +++++++++ .../resources/db/oracle/clients_oracle.sql | 61 +++++++++++++++ .../resources/db/oracle/scopes_oracle.sql | 31 ++++++++ .../src/main/resources/db/psql/clients.sql | 74 +++++++++++++++++++ .../src/main/resources/db/psql/scopes.sql | 33 +++++++++ 8 files changed, 303 insertions(+), 2 deletions(-) rename uma-server-webapp/src/main/resources/db/{ => hsql}/clients.sql (99%) mode change 100644 => 100755 rename uma-server-webapp/src/main/resources/db/{ => hsql}/scopes.sql (98%) mode change 100644 => 100755 create mode 100755 uma-server-webapp/src/main/resources/db/mysql/clients.sql create mode 100755 uma-server-webapp/src/main/resources/db/mysql/scopes.sql create mode 100755 uma-server-webapp/src/main/resources/db/oracle/clients_oracle.sql create mode 100755 uma-server-webapp/src/main/resources/db/oracle/scopes_oracle.sql create mode 100755 uma-server-webapp/src/main/resources/db/psql/clients.sql create mode 100755 uma-server-webapp/src/main/resources/db/psql/scopes.sql diff --git a/uma-server-webapp/src/main/resources/db/clients.sql b/uma-server-webapp/src/main/resources/db/hsql/clients.sql old mode 100644 new mode 100755 similarity index 99% rename from uma-server-webapp/src/main/resources/db/clients.sql rename to uma-server-webapp/src/main/resources/db/hsql/clients.sql index cb8a6c2367..8d41bcad94 --- a/uma-server-webapp/src/main/resources/db/clients.sql +++ b/uma-server-webapp/src/main/resources/db/hsql/clients.sql @@ -28,7 +28,7 @@ INSERT INTO client_scope_TEMP (owner_id, scope) VALUES INSERT INTO client_redirect_uri_TEMP (owner_id, redirect_uri) VALUES ('client', 'http://localhost/'), ('client', 'http://localhost:8080/'); - + INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES ('client', 'authorization_code'), ('client', 'urn:ietf:params:oauth:grant_type:redelegate'), diff --git a/uma-server-webapp/src/main/resources/db/scopes.sql b/uma-server-webapp/src/main/resources/db/hsql/scopes.sql old mode 100644 new mode 100755 similarity index 98% rename from uma-server-webapp/src/main/resources/db/scopes.sql rename to uma-server-webapp/src/main/resources/db/hsql/scopes.sql index e0345dce04..9b0446af51 --- a/uma-server-webapp/src/main/resources/db/scopes.sql +++ b/uma-server-webapp/src/main/resources/db/hsql/scopes.sql @@ -32,4 +32,4 @@ MERGE INTO system_scope COMMIT; -SET AUTOCOMMIT TRUE; \ No newline at end of file +SET AUTOCOMMIT TRUE; diff --git a/uma-server-webapp/src/main/resources/db/mysql/clients.sql b/uma-server-webapp/src/main/resources/db/mysql/clients.sql new file mode 100755 index 0000000000..02444c4732 --- /dev/null +++ b/uma-server-webapp/src/main/resources/db/mysql/clients.sql @@ -0,0 +1,69 @@ +-- +-- Turn off autocommit and start a transaction so that we can use the temp tables +-- + +SET AUTOCOMMIT = 0; + +START TRANSACTION; + +-- +-- Insert client information into the temporary tables. To add clients to the HSQL database, edit things here. +-- + +INSERT INTO client_details_TEMP (client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection) VALUES + ('client', 'secret', 'Test Client', false, null, 3600, 600, true), + ('rs', 'secret', 'Test UMA RS', false, null, null, 600, false), + ('c', 'secret', 'Test UMA Client', false, null, null, 600, false); + +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES + ('client', 'openid'), + ('client', 'profile'), + ('client', 'email'), + ('client', 'address'), + ('client', 'phone'), + ('client', 'offline_access'), + ('rs', 'uma_protection'), + ('c', 'uma_authorization'); + +INSERT INTO client_redirect_uri_TEMP (owner_id, redirect_uri) VALUES + ('client', 'http://localhost/'), + ('client', 'http://localhost:8080/'); + +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES + ('client', 'authorization_code'), + ('client', 'urn:ietf:params:oauth:grant_type:redelegate'), + ('client', 'implicit'), + ('client', 'refresh_token'), + ('rs', 'authorization_code'), + ('rs', 'implicit'), + ('c', 'authorization_code'), + ('c', 'implicit'); + +-- +-- Merge the temporary clients safely into the database. This is a two-step process to keep clients from being created on every startup with a persistent store. +-- + +INSERT INTO client_details (client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection) + SELECT client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection FROM client_details_TEMP + ON DUPLICATE KEY UPDATE client_details.client_id = client_details.client_id; + +INSERT INTO client_scope (owner_id, scope) + SELECT id, scope FROM client_scope_TEMP, client_details WHERE client_details.client_id = client_scope_TEMP.owner_id + ON DUPLICATE KEY UPDATE client_scope.owner_id = client_scope.owner_id; + +INSERT INTO client_redirect_uri (owner_id, redirect_uri) + SELECT id, redirect_uri FROM client_redirect_uri_TEMP, client_details WHERE client_details.client_id = client_redirect_uri_TEMP.owner_id + ON DUPLICATE KEY UPDATE client_redirect_uri.owner_id = client_redirect_uri.owner_id; + +INSERT INTO client_grant_type (owner_id, grant_type) + SELECT id, grant_type FROM client_grant_type_TEMP, client_details WHERE client_details.client_id = client_grant_type_TEMP.owner_id + ON DUPLICATE KEY UPDATE client_grant_type.owner_id = client_grant_type.owner_id; + +-- +-- Close the transaction and turn autocommit back on +-- + +COMMIT; + +SET AUTOCOMMIT = 1; + diff --git a/uma-server-webapp/src/main/resources/db/mysql/scopes.sql b/uma-server-webapp/src/main/resources/db/mysql/scopes.sql new file mode 100755 index 0000000000..bdcc0f6e30 --- /dev/null +++ b/uma-server-webapp/src/main/resources/db/mysql/scopes.sql @@ -0,0 +1,33 @@ +-- +-- Turn off autocommit and start a transaction so that we can use the temp tables +-- + +SET AUTOCOMMIT = 0; + +START TRANSACTION; + +-- +-- Insert scope information into the temporary tables. +-- + +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('openid', 'log in using your identity', 'user', false, true, false, null), + ('profile', 'basic profile information', 'list-alt', false, true, false, null), + ('email', 'email address', 'envelope', false, true, false, null), + ('address', 'physical address', 'home', false, true, false, null), + ('phone', 'telephone number', 'bell', false, true, false, null), + ('offline_access', 'offline access', 'time', false, false, false, null), + ('uma_protection', 'manage protected resources', 'briefcase', false, false, false, null), + ('uma_authorization', 'request access to protected resources', 'share', false, false, false, null); + +-- +-- Merge the temporary scopes safely into the database. This is a two-step process to keep scopes from being created on every startup with a persistent store. +-- + +INSERT INTO system_scope (scope, description, icon, restricted, default_scope, structured, structured_param_description) + SELECT scope, description, icon, restricted, default_scope, structured, structured_param_description FROM system_scope_TEMP + ON DUPLICATE KEY UPDATE system_scope.scope = system_scope.scope; + +COMMIT; + +SET AUTOCOMMIT = 1; diff --git a/uma-server-webapp/src/main/resources/db/oracle/clients_oracle.sql b/uma-server-webapp/src/main/resources/db/oracle/clients_oracle.sql new file mode 100755 index 0000000000..783ff2d3a4 --- /dev/null +++ b/uma-server-webapp/src/main/resources/db/oracle/clients_oracle.sql @@ -0,0 +1,61 @@ +-- +-- Insert client information into the temporary tables. To add clients to the Oracle database, edit things here. +-- + +INSERT INTO client_details_TEMP (client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection) VALUES + ('client', 'secret', 'Test Client', 0, null, 3600, 600, 1); +INSERT INTO client_details_TEMP (client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection) VALUES + ('rs', 'secret', 'Test UMA RS', false, null, null, 600, false); +INSERT INTO client_details_TEMP (client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection) VALUES + ('c', 'secret', 'Test UMA Client', false, null, null, 600, false); + +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES ('client', 'openid'); +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES ('client', 'profile'); +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES ('client', 'email'); +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES ('client', 'address'); +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES ('client', 'phone'); +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES ('client', 'offline_access'); +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES ('rs', 'uma_protection'); +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES ('c', 'uma_authorization'); + +INSERT INTO client_redirect_uri_TEMP (owner_id, redirect_uri) VALUES ('client', 'http://localhost/'); +INSERT INTO client_redirect_uri_TEMP (owner_id, redirect_uri) VALUES ('client', 'http://localhost:8080/'); + +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES ('client', 'authorization_code'); +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES ('client', 'urn:ietf:params:oauth:grant_type:redelegate'); +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES ('client', 'implicit'); +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES ('client', 'refresh_token'); +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES ('rs', 'authorization_code'); +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES ('rs', 'implicit'); +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES ('c', 'authorization_code'); +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES ('c', 'implicit'); + +-- +-- Merge the temporary clients safely into the database. This is a two-step process to keep clients from being created on every startup with a persistent store. +-- + +MERGE INTO client_details + USING (SELECT client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection FROM client_details_TEMP) vals + ON (vals.client_id = client_details.client_id) + WHEN NOT MATCHED THEN + INSERT (id, client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, + id_token_validity_seconds, allow_introspection) VALUES(client_details_seq.nextval, vals.client_id, vals.client_secret, vals.client_name, vals.dynamically_registered, + vals.refresh_token_validity_seconds, vals.access_token_validity_seconds, vals.id_token_validity_seconds, vals.allow_introspection); + +MERGE INTO client_scope + USING (SELECT id, scope FROM client_scope_TEMP, client_details WHERE client_details.client_id = client_scope_TEMP.owner_id) vals + ON (vals.id = client_scope.owner_id AND vals.scope = client_scope.scope) + WHEN NOT MATCHED THEN + INSERT (owner_id, scope) values (vals.id, vals.scope); + +MERGE INTO client_redirect_uri + USING (SELECT id, redirect_uri FROM client_redirect_uri_TEMP, client_details WHERE client_details.client_id = client_redirect_uri_TEMP.owner_id) vals + ON (vals.id = client_redirect_uri.owner_id AND vals.redirect_uri = client_redirect_uri.redirect_uri) + WHEN NOT MATCHED THEN + INSERT (owner_id, redirect_uri) values (vals.id, vals.redirect_uri); + +MERGE INTO client_grant_type + USING (SELECT id, grant_type FROM client_grant_type_TEMP, client_details WHERE client_details.client_id = client_grant_type_TEMP.owner_id) vals + ON (vals.id = client_grant_type.owner_id AND vals.grant_type = client_grant_type.grant_type) + WHEN NOT MATCHED THEN + INSERT (owner_id, grant_type) values (vals.id, vals.grant_type); diff --git a/uma-server-webapp/src/main/resources/db/oracle/scopes_oracle.sql b/uma-server-webapp/src/main/resources/db/oracle/scopes_oracle.sql new file mode 100755 index 0000000000..a52e021dea --- /dev/null +++ b/uma-server-webapp/src/main/resources/db/oracle/scopes_oracle.sql @@ -0,0 +1,31 @@ +-- +-- Insert scope information into the temporary tables. +-- + +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('openid', 'log in using your identity', 'user', 0, 1, 0, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('profile', 'basic profile information', 'list-alt', 0, 1, 0, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('email', 'email address', 'envelope', 0, 1, 0, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('address', 'physical address', 'home', 0, 1, 0, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('phone', 'telephone number', 'bell', 0, 1, 0, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('offline_access', 'offline access', 'time', 0, 0, 0, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('uma_protection', 'manage protected resources', 'briefcase', 0, 0, 0, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('uma_authorization', 'request access to protected resources', 'share', 0, 0, 0, null); + +-- +-- Merge the temporary scopes safely into the database. This is a two-step process to keep scopes from being created on every startup with a persistent store. +-- + +MERGE INTO system_scope + USING (SELECT scope, description, icon, restricted, default_scope, structured, structured_param_description FROM system_scope_TEMP) vals + ON (vals.scope = system_scope.scope) + WHEN NOT MATCHED THEN + INSERT (id, scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES(system_scope_seq.nextval, vals.scope, + vals.description, vals.icon, vals.restricted, vals.default_scope, vals.structured, vals.structured_param_description); diff --git a/uma-server-webapp/src/main/resources/db/psql/clients.sql b/uma-server-webapp/src/main/resources/db/psql/clients.sql new file mode 100755 index 0000000000..d4c75e7fe6 --- /dev/null +++ b/uma-server-webapp/src/main/resources/db/psql/clients.sql @@ -0,0 +1,74 @@ +-- +-- Turn off autocommit and start a transaction so that we can use the temp tables +-- + +--SET AUTOCOMMIT = OFF; + +START TRANSACTION; + +-- +-- Insert client information into the temporary tables. To add clients to the HSQL database, edit things here. +-- + +INSERT INTO client_details_TEMP (client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection) VALUES + ('client', 'secret', 'Test Client', false, null, 3600, 600, true), + ('rs', 'secret', 'Test UMA RS', false, null, null, 600, false), + ('c', 'secret', 'Test UMA Client', false, null, null, 600, false); + +INSERT INTO client_scope_TEMP (owner_id, scope) VALUES + ('client', 'openid'), + ('client', 'profile'), + ('client', 'email'), + ('client', 'address'), + ('client', 'phone'), + ('client', 'offline_access'), + ('rs', 'uma_protection'), + ('c', 'uma_authorization'); + +INSERT INTO client_redirect_uri_TEMP (owner_id, redirect_uri) VALUES + ('client', 'http://localhost/'), + ('client', 'http://localhost:8080/'); + +INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES + ('client', 'authorization_code'), + ('client', 'urn:ietf:params:oauth:grant_type:redelegate'), + ('client', 'implicit'), + ('client', 'refresh_token'), + ('rs', 'authorization_code'), + ('rs', 'implicit'), + ('c', 'authorization_code'), + ('c', 'implicit'); + +-- +-- Merge the temporary clients safely into the database. This is a two-step process to keep clients from being created on every startup with a persistent store. +-- + +INSERT INTO client_details (client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection) + SELECT client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection FROM client_details_TEMP + ON CONFLICT + DO NOTHING; + +INSERT INTO client_scope (scope) + SELECT scope FROM client_scope_TEMP, client_details WHERE client_details.client_id = client_scope_TEMP.owner_id + ON CONFLICT + DO NOTHING; + +INSERT INTO client_redirect_uri (redirect_uri) + SELECT redirect_uri FROM client_redirect_uri_TEMP, client_details WHERE client_details.client_id = client_redirect_uri_TEMP.owner_id + ON CONFLICT + DO NOTHING; + +INSERT INTO client_grant_type (grant_type) + SELECT grant_type FROM client_grant_type_TEMP, client_details WHERE client_details.client_id = client_grant_type_TEMP.owner_id + ON CONFLICT + DO NOTHING; + +-- +-- Close the transaction and turn autocommit back on +-- + +COMMIT; + +--SET AUTOCOMMIT = ON; + + diff --git a/uma-server-webapp/src/main/resources/db/psql/scopes.sql b/uma-server-webapp/src/main/resources/db/psql/scopes.sql new file mode 100755 index 0000000000..8b2611b832 --- /dev/null +++ b/uma-server-webapp/src/main/resources/db/psql/scopes.sql @@ -0,0 +1,33 @@ +-- +-- Turn off autocommit and start a transaction so that we can use the temp tables +-- + +--SET AUTOCOMMIT = OFF; + +START TRANSACTION; + +-- +-- Insert scope information into the temporary tables. +-- + +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES + ('openid', 'log in using your identity', 'user', false, true, false, null), + ('profile', 'basic profile information', 'list-alt', false, true, false, null), + ('email', 'email address', 'envelope', false, true, false, null), + ('address', 'physical address', 'home', false, true, false, null), + ('phone', 'telephone number', 'bell', false, true, false, null), + ('offline_access', 'offline access', 'time', false, false, false, null); + +-- +-- Merge the temporary scopes safely into the database. This is a two-step process to keep scopes from being created on every startup with a persistent store. +-- + +INSERT INTO system_scope (scope, description, icon, restricted, default_scope, structured, structured_param_description) + SELECT scope, description, icon, restricted, default_scope, structured, structured_param_description FROM system_scope_TEMP + ON CONFLICT(scope) + DO NOTHING; + +COMMIT; + +--SET AUTOCOMMIT = ON; + From 0c09a17f59f59372a7263df86b0ab995a9cbc261 Mon Sep 17 00:00:00 2001 From: Sebastian Schuster Date: Thu, 12 Jan 2017 18:01:49 +0100 Subject: [PATCH 105/269] Adapted uma-server-webapp overlayed spring configuration files to changes in base webapp --- .../webapp/WEB-INF/application-context.xml | 33 +++++++++++++------ .../src/main/webapp/WEB-INF/server-config.xml | 2 +- .../src/main/webapp/WEB-INF/user-context.xml | 22 ++++++++----- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml b/uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml index 7de114acb1..9cb095fffe 100644 --- a/uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml +++ b/uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml @@ -25,7 +25,7 @@ xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd @@ -86,66 +86,77 @@ +
+ + + - + + - + + - + + - + + - + + - + + - + + - + + - + + diff --git a/uma-server-webapp/src/main/webapp/WEB-INF/server-config.xml b/uma-server-webapp/src/main/webapp/WEB-INF/server-config.xml index df1fe557ff..36a2de70e6 100644 --- a/uma-server-webapp/src/main/webapp/WEB-INF/server-config.xml +++ b/uma-server-webapp/src/main/webapp/WEB-INF/server-config.xml @@ -24,7 +24,7 @@ xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> diff --git a/uma-server-webapp/src/main/webapp/WEB-INF/user-context.xml b/uma-server-webapp/src/main/webapp/WEB-INF/user-context.xml index ce4e945afe..827b53141c 100644 --- a/uma-server-webapp/src/main/webapp/WEB-INF/user-context.xml +++ b/uma-server-webapp/src/main/webapp/WEB-INF/user-context.xml @@ -24,7 +24,7 @@ xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> @@ -47,7 +47,7 @@ - + @@ -110,7 +110,7 @@ - + @@ -119,19 +119,25 @@ - + + + - - + + - + + + + + From ffc5782810f62d0a87db8c96ae95bc209bf2de9a Mon Sep 17 00:00:00 2001 From: juazugas Date: Fri, 14 Oct 2016 13:09:48 +0200 Subject: [PATCH 106/269] Fix field definition in the model object. --- .../src/main/webapp/resources/js/client.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js index b33b7b4c16..03837b0915 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js @@ -586,7 +586,7 @@ var ClientFormView = Backbone.View.extend({ this.redirectUrisCollection = new Backbone.Collection(); this.scopeCollection = new Backbone.Collection(); this.contactsCollection = new Backbone.Collection(); - this.defaultAcrValuesCollection = new Backbone.Collection(); + this.defaultACRvaluesCollection = new Backbone.Collection(); this.requestUrisCollection = new Backbone.Collection(); this.postLogoutRedirectUrisCollection = new Backbone.Collection(); this.claimsRedirectUrisCollection = new Backbone.Collection(); @@ -949,7 +949,7 @@ var ClientFormView = Backbone.View.extend({ defaultMaxAge: parseInt($('#defaultMaxAge input').val()), contacts: this.contactsCollection.pluck('item'), requestUris: this.requestUrisCollection.pluck('item'), - defaultAcrValues: this.defaultAcrValuesCollection.pluck('item'), + defaultACRvalues: this.defaultACRvaluesCollection.pluck('item'), requestObjectSigningAlg: this.defaultToNull($('#requestObjectSigningAlg select').val()), userInfoSignedResponseAlg: this.defaultToNull($('#userInfoSignedResponseAlg select').val()), userInfoEncryptedResponseAlg: this.defaultToNull($('#userInfoEncryptedResponseAlg select').val()), @@ -1109,15 +1109,15 @@ var ClientFormView = Backbone.View.extend({ this.listWidgetViews.push(requestUriView); // build and bind default ACR values - _.each(this.model.get('defaultAcrValues'), function (defaultAcrValue) { - _self.defaultAcrValuesCollection.add(new Backbone.Model({item:defaultAcrValue})); + _.each(this.model.get('defaultACRvalues'), function (defaultACRvalue) { + _self.defaultACRvaluesCollection.add(new Backbone.Model({item:defaultACRvalue})); }); var defaultAcrView = new ListWidgetView({ placeholder: $.t('client.client-form.acr-values-placeholder'), // TODO: autocomplete from spec helpBlockText: $.t('client.client-form.acr-values-help'), - collection: this.defaultAcrValuesCollection}); + collection: this.defaultACRvaluesCollection}); $('#defaultAcrValues .controls', this.el).html(defaultAcrView.render().el); this.listWidgetViews.push(defaultAcrView); From 099211593c09d109af30229be0c0ad7a7bccceee Mon Sep 17 00:00:00 2001 From: strangeweaver Date: Thu, 4 Feb 2016 17:44:25 +0000 Subject: [PATCH 107/269] Fix high load performance issue in token expiration task --- .../repository/OAuth2TokenRepository.java | 20 +++++++++++++++++++ .../impl/JpaOAuth2TokenRepository.java | 4 ++-- .../DefaultOAuth2ProviderTokenService.java | 4 ---- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java index 79f167c511..5afc5def31 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java @@ -61,8 +61,28 @@ public interface OAuth2TokenRepository { public Set getAccessTokensForResourceSet(ResourceSet rs); + /** + * removes duplicate access tokens. + * + * @deprecated this method was added to return the remove duplicate access tokens values + * so that {code removeAccessToken(OAuth2AccessTokenEntity o)} would not to fail. the + * removeAccessToken method has been updated so as it will not fail in the event that an + * accessToken has been duplicated, so this method is unnecessary. + * + */ + @Deprecated public void clearDuplicateAccessTokens(); + /** + * removes duplicate refresh tokens. + * + * @deprecated this method was added to return the remove duplicate refresh token value + * so that {code removeRefreshToken(OAuth2RefreshTokenEntity o)} would not to fail. the + * removeRefreshToken method has been updated so as it will not fail in the event that + * refreshToken has been duplicated, so this method is unnecessary. + * + */ + @Deprecated public void clearDuplicateRefreshTokens(); public List getAccessTokensForApprovedSite(ApprovedSite approvedSite); diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java index eb30cbe455..d10480e16d 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java @@ -95,7 +95,7 @@ public OAuth2AccessTokenEntity saveAccessToken(OAuth2AccessTokenEntity token) { @Override @Transactional(value="defaultTransactionManager") public void removeAccessToken(OAuth2AccessTokenEntity accessToken) { - OAuth2AccessTokenEntity found = getAccessTokenByValue(accessToken.getValue()); + OAuth2AccessTokenEntity found = getAccessTokenById(accessToken.getId()); if (found != null) { manager.remove(found); } else { @@ -140,7 +140,7 @@ public OAuth2RefreshTokenEntity saveRefreshToken(OAuth2RefreshTokenEntity refres @Override @Transactional(value="defaultTransactionManager") public void removeRefreshToken(OAuth2RefreshTokenEntity refreshToken) { - OAuth2RefreshTokenEntity found = getRefreshTokenByValue(refreshToken.getValue()); + OAuth2RefreshTokenEntity found = getRefreshTokenById(refreshToken.getId()); if (found != null) { manager.remove(found); } else { diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java index b358712181..8bfcd970b4 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java @@ -489,10 +489,6 @@ public List getRefreshTokensForClient(ClientDetailsEnt @Override public void clearExpiredTokens() { logger.debug("Cleaning out all expired tokens"); - - // get all the duplicated tokens first to maintain consistency - tokenRepository.clearDuplicateAccessTokens(); - tokenRepository.clearDuplicateRefreshTokens(); Collection accessTokens = getExpiredAccessTokens(); if (accessTokens.size() > 0) { From 46046b574a80d686cf145587a858bd8844834701 Mon Sep 17 00:00:00 2001 From: strangeweaver Date: Thu, 18 Feb 2016 12:39:05 +0000 Subject: [PATCH 108/269] Implemented paged operations and used for database cleanup tasks. --- .../data/AbstractPageOperationTemplate.java | 160 ++++++++++++++ .../org/mitre/data/DefaultPageCriteria.java | 35 +++ .../java/org/mitre/data/PageCriteria.java | 13 ++ .../AuthenticationHolderRepository.java | 3 +- .../AuthorizationCodeRepository.java | 7 + .../repository/OAuth2TokenRepository.java | 5 + .../main/java/org/mitre/util/jpa/JpaUtil.java | 24 ++- .../AbstractPageOperationTemplateTest.java | 204 ++++++++++++++++++ .../JpaAuthenticationHolderRepository.java | 15 +- .../impl/JpaAuthorizationCodeRepository.java | 9 + .../impl/JpaOAuth2TokenRepository.java | 30 ++- ...DefaultOAuth2AuthorizationCodeService.java | 21 +- .../DefaultOAuth2ProviderTokenService.java | 78 ++++--- 13 files changed, 540 insertions(+), 64 deletions(-) create mode 100644 openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java create mode 100644 openid-connect-common/src/main/java/org/mitre/data/DefaultPageCriteria.java create mode 100644 openid-connect-common/src/main/java/org/mitre/data/PageCriteria.java create mode 100644 openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java diff --git a/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java b/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java new file mode 100644 index 0000000000..3a8962aacf --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java @@ -0,0 +1,160 @@ +package org.mitre.data; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * Abstract class for performing an operation on a potentially large + * number of items by paging through the items in discreet chunks. + * + * @param the type parameter + * @author Colm Smyth. + */ +public abstract class AbstractPageOperationTemplate { + + private static final Logger logger = LoggerFactory.getLogger(AbstractPageOperationTemplate.class); + + private static int DEFAULT_MAX_PAGES = 1000; + private static long DEFAULT_MAX_TIME_MILLIS = 600000L; //10 Minutes + + /** + * int specifying the maximum number of + * pages which should be fetched before + * execution should terminate + */ + private int maxPages; + + /** + * long specifying the maximum execution time + * in milliseconds + */ + private long maxTime; + + /** + * boolean specifying whether or not Exceptions + * incurred performing the operation should be + * swallowed during execution default true. + */ + private boolean swallowExceptions = true; + + + /** + * default constructor which sets the value of + * maxPages and maxTime to DEFAULT_MAX_PAGES and + * DEFAULT_MAX_TIME_MILLIS respectively + */ + public AbstractPageOperationTemplate(){ + this(DEFAULT_MAX_PAGES, DEFAULT_MAX_TIME_MILLIS); + } + + + /** + * Instantiates a new AbstractPageOperationTemplate with the + * given maxPages and maxTime + * + * @param maxPages the maximum number of pages to fetch. + * @param maxTime the maximum execution time. + */ + public AbstractPageOperationTemplate(int maxPages, long maxTime){ + this.maxPages = maxPages; + this.maxTime = maxTime; + } + + + /** + * Execute the operation on each member of a page of results + * retrieved through the fetch method. the method will execute + * until either the maxPages or maxTime limit is reached or until + * the fetch method returns no more results. Exceptions thrown + * performing the operation on the item will be swallowed if the + * swallowException (default true) field is set true. + */ + public void execute(){ + logger.info("Starting execution of paged operation. maximum time: " + maxTime + + " maximum pages: " + maxPages); + + long startTime = System.currentTimeMillis(); + long executionTime = 0; + int i = 0; + + int exceptionsSwallowedCount = 0; + int operationsCompleted = 0; + Set exceptionsSwallowedClasses = new HashSet(); + + + while (i< maxPages && executionTime < maxTime){ + Collection page = fetchPage(); + if(page == null || page.size() == 0){ + break; + } + + for (T item : page) { + try { + doOperation(item); + operationsCompleted++; + } catch (Exception e){ + if(swallowExceptions){ + exceptionsSwallowedCount++; + exceptionsSwallowedClasses.add(e.getClass().getName()); + logger.debug("Swallowing exception " + e.getMessage(), e); + } else { + logger.debug("Rethrowing exception " + e.getMessage()); + throw e; + } + } + } + + i++; + executionTime = System.currentTimeMillis() - startTime; + } + + logger.info("Paged operation run completed " + operationsCompleted + " swallowed " + exceptionsSwallowedCount + " exceptions"); + for(String className: exceptionsSwallowedClasses) { + logger.warn("Paged operation swallowed at least one exception of type " + className); + } + } + + /** + * method responsible for fetching + * a page of items. + * + * @return the collection of items + */ + public abstract Collection fetchPage(); + + /** + * method responsible for performing desired + * operation on a fetched page item. + * + * @param item the item + */ + protected abstract void doOperation(T item); + + public int getMaxPages() { + return maxPages; + } + + public void setMaxPages(int maxPages) { + this.maxPages = maxPages; + } + + public long getMaxTime() { + return maxTime; + } + + public void setMaxTime(long maxTime) { + this.maxTime = maxTime; + } + + public boolean isSwallowExceptions() { + return swallowExceptions; + } + + public void setSwallowExceptions(boolean swallowExceptions) { + this.swallowExceptions = swallowExceptions; + } +} diff --git a/openid-connect-common/src/main/java/org/mitre/data/DefaultPageCriteria.java b/openid-connect-common/src/main/java/org/mitre/data/DefaultPageCriteria.java new file mode 100644 index 0000000000..fc559c5e4d --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/data/DefaultPageCriteria.java @@ -0,0 +1,35 @@ +package org.mitre.data; + +/** + * Default implementation of PageCriteria which specifies + * both page to be retrieved and page size in the constructor. + * + * @author Colm Smyth + */ +public class DefaultPageCriteria implements PageCriteria { + + private static final int DEFAULT_PAGE_NUMBER = 0; + private static final int DEFAULT_PAGE_SIZE = 100; + + private int pageNumber; + private int pageSize; + + public DefaultPageCriteria(){ + this(DEFAULT_PAGE_NUMBER, DEFAULT_PAGE_SIZE); + } + + public DefaultPageCriteria(int pageNumber, int pageSize) { + this.pageNumber = pageNumber; + this.pageSize = pageSize; + } + + @Override + public int getPageNumber() { + return pageNumber; + } + + @Override + public int getPageSize() { + return pageSize; + } +} diff --git a/openid-connect-common/src/main/java/org/mitre/data/PageCriteria.java b/openid-connect-common/src/main/java/org/mitre/data/PageCriteria.java new file mode 100644 index 0000000000..f9f57562f2 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/data/PageCriteria.java @@ -0,0 +1,13 @@ +package org.mitre.data; + +/** + * Interface which defines page criteria for use in + * a repository operation. + * + * @author Colm Smyth + */ +public interface PageCriteria { + + public int getPageNumber(); + public int getPageSize(); +} diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthenticationHolderRepository.java b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthenticationHolderRepository.java index 3725e6e8c7..af811430d1 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthenticationHolderRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthenticationHolderRepository.java @@ -18,6 +18,7 @@ import java.util.List; +import org.mitre.data.PageCriteria; import org.mitre.oauth2.model.AuthenticationHolderEntity; public interface AuthenticationHolderRepository { @@ -31,5 +32,5 @@ public interface AuthenticationHolderRepository { public List getOrphanedAuthenticationHolders(); - + public List getOrphanedAuthenticationHolders(PageCriteria pageCriteria); } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthorizationCodeRepository.java b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthorizationCodeRepository.java index e98c52f235..9d056abbe9 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthorizationCodeRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthorizationCodeRepository.java @@ -18,6 +18,7 @@ import java.util.Collection; +import org.mitre.data.PageCriteria; import org.mitre.oauth2.model.AuthorizationCodeEntity; /** @@ -56,4 +57,10 @@ public interface AuthorizationCodeRepository { */ public Collection getExpiredCodes(); + /** + * @return A collection of all expired codes, limited by the given + * PageCriteria. + */ + public Collection getExpiredCodes(PageCriteria pageCriteria); + } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java index 5afc5def31..9c4ac7b267 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Set; +import org.mitre.data.PageCriteria; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.oauth2.model.OAuth2RefreshTokenEntity; @@ -57,8 +58,12 @@ public interface OAuth2TokenRepository { public Set getAllExpiredAccessTokens(); + public Set getAllExpiredAccessTokens(PageCriteria pageCriteria); + public Set getAllExpiredRefreshTokens(); + public Set getAllExpiredRefreshTokens(PageCriteria pageCriteria); + public Set getAccessTokensForResourceSet(ResourceSet rs); /** diff --git a/openid-connect-common/src/main/java/org/mitre/util/jpa/JpaUtil.java b/openid-connect-common/src/main/java/org/mitre/util/jpa/JpaUtil.java index 2ac846360b..0295c691b7 100644 --- a/openid-connect-common/src/main/java/org/mitre/util/jpa/JpaUtil.java +++ b/openid-connect-common/src/main/java/org/mitre/util/jpa/JpaUtil.java @@ -16,9 +16,12 @@ *******************************************************************************/ package org.mitre.util.jpa; +import org.mitre.data.PageCriteria; + import java.util.List; import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; /** * @author mfranklin @@ -26,7 +29,7 @@ * Time: 2:13 PM */ public class JpaUtil { - public static T getSingleResult(List list) { + public static T getSingleResult(List list) { switch(list.size()) { case 0: return null; @@ -37,6 +40,25 @@ public static T getSingleResult(List list) { } } + + /** + * Get a page of results from the specified TypedQuery + * by using the given PageCriteria to limit the query + * results. The PageCriteria will override any size or + * offset already specified on the query. + * + * @param the type parameter + * @param query the query + * @param pageCriteria the page criteria + * @return the list + */ + public static List getResultPage(TypedQuery query, PageCriteria pageCriteria){ + query.setMaxResults(pageCriteria.getPageSize()); + query.setFirstResult(pageCriteria.getPageNumber()*pageCriteria.getPageSize()); + + return query.getResultList(); + } + public static T saveOrUpdate(I id, EntityManager entityManager, T entity) { T tmp = entityManager.merge(entity); entityManager.flush(); diff --git a/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java b/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java new file mode 100644 index 0000000000..1aaa46869c --- /dev/null +++ b/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java @@ -0,0 +1,204 @@ +package org.mitre.data; + +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author Colm Smyth + */ +public class AbstractPageOperationTemplateTest { + + @Before + public void setUp() throws Exception { + } + + @Test + public void execute_zeropages() { + CountingPageOperation op = new CountingPageOperation(0,Long.MAX_VALUE); + op.execute(); + + assertEquals(0L, op.counter); + } + + @Test + public void execute_singlepage() { + CountingPageOperation op = new CountingPageOperation(1,Long.MAX_VALUE); + op.execute(); + + assertEquals(10L, op.counter); + } + + @Test + public void execute_negpage() { + CountingPageOperation op = new CountingPageOperation(-1,Long.MAX_VALUE); + op.execute(); + + assertEquals(0L, op.counter); + } + + @Test + public void execute_npage(){ + int n = 7; + CountingPageOperation op = new CountingPageOperation(n,Long.MAX_VALUE); + op.execute(); + + assertEquals(n*10L, op.counter); + } + + @Test + public void execute_nullpage(){ + CountingPageOperation op = new NullPageCountingPageOperation(Integer.MAX_VALUE, Long.MAX_VALUE); + op.execute(); + + assertEquals(0L, op.getCounter()); + } + + @Test + public void execute_emptypage(){ + CountingPageOperation op = new EmptyPageCountingPageOperation(Integer.MAX_VALUE, Long.MAX_VALUE); + op.execute(); + + assertEquals(0L, op.getCounter()); + } + + @Test + public void execute_zerotime(){ + CountingPageOperation op = new CountingPageOperation(Integer.MAX_VALUE,0L); + op.execute(); + + assertEquals(0L, op.getCounter()); + assertEquals(0L, op.getLastFetchTime()); + } + + @Test + public void execute_nonzerotime(){ + Long timeMillis = 100L; + CountingPageOperation op = new CountingPageOperation(Integer.MAX_VALUE,timeMillis); + op.execute(); + + assertTrue("start time " + op.getStartTime() + "" + + " to last fetch time " + op.getLastFetchTime() + + " exceeds max time" + timeMillis, op.getLastFetchTime() - op.getStartTime() <= timeMillis); + } + + @Test + public void execute_negtime(){ + Long timeMillis = -100L; + CountingPageOperation op = new CountingPageOperation(Integer.MAX_VALUE,timeMillis); + op.execute(); + + assertEquals(0L, op.getCounter()); + } + + @Test + public void execute_swallowException(){ + CountingPageOperation op = new EvenExceptionCountingPageOperation(1, 1000L); + op.execute(); + + assertTrue(op.isSwallowExceptions()); + assertEquals(5L, op.getCounter()); + } + + @Test(expected = IllegalStateException.class) + public void execute_noSwallowException(){ + CountingPageOperation op = new EvenExceptionCountingPageOperation(1, 1000L); + op.setSwallowExceptions(false); + + try { + op.execute(); + }finally { + assertEquals(1L, op.getCounter()); + } + } + + + private static class CountingPageOperation extends AbstractPageOperationTemplate{ + + private int currentPageFetch; + private int pageSize = 10; + private long counter = 0L; + private long startTime; + private long lastFetchTime; + + private CountingPageOperation(int maxPages, long maxTime) { + super(maxPages, maxTime); + startTime = System.currentTimeMillis(); + } + + @Override + public Collection fetchPage() { + lastFetchTime = System.currentTimeMillis(); + List page = new ArrayList<>(pageSize); + for(int i = 0; i < pageSize; i++ ) { + page.add("item " + currentPageFetch * pageSize + i); + } + currentPageFetch++; + return page; + } + + @Override + protected void doOperation(String item) { + counter++; + } + + public long getCounter() { + return counter; + } + + public long getLastFetchTime() { + return lastFetchTime; + } + + public long getStartTime(){ + return startTime; + } + } + + private static class NullPageCountingPageOperation extends CountingPageOperation { + private NullPageCountingPageOperation(int maxPages, long maxTime) { + super(maxPages, maxTime); + } + + @Override + public Collection fetchPage() { + return null; + } + } + + private static class EmptyPageCountingPageOperation extends CountingPageOperation { + private EmptyPageCountingPageOperation(int maxPages, long maxTime) { + super(maxPages, maxTime); + } + + @Override + public Collection fetchPage() { + return new ArrayList<>(0); + } + } + + private static class EvenExceptionCountingPageOperation extends CountingPageOperation { + + private int callCounter; + private EvenExceptionCountingPageOperation(int maxPages, long maxTime) { + super(maxPages, maxTime); + } + + @Override + protected void doOperation(String item) { + callCounter++; + if(callCounter%2 == 0){ + throw new IllegalStateException("even number items cannot be processed"); + } + + super.doOperation(item); + + } + } +} diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthenticationHolderRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthenticationHolderRepository.java index 602302c6e7..7597c79b62 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthenticationHolderRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthenticationHolderRepository.java @@ -22,6 +22,8 @@ import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; +import org.mitre.data.DefaultPageCriteria; +import org.mitre.data.PageCriteria; import org.mitre.oauth2.model.AuthenticationHolderEntity; import org.mitre.oauth2.repository.AuthenticationHolderRepository; import org.mitre.util.jpa.JpaUtil; @@ -68,10 +70,15 @@ public AuthenticationHolderEntity save(AuthenticationHolderEntity a) { @Override @Transactional(value="defaultTransactionManager") public List getOrphanedAuthenticationHolders() { - TypedQuery query = manager.createNamedQuery(AuthenticationHolderEntity.QUERY_GET_UNUSED, AuthenticationHolderEntity.class); - query.setMaxResults(MAXEXPIREDRESULTS); - List unusedAuthenticationHolders = query.getResultList(); - return unusedAuthenticationHolders; + DefaultPageCriteria pageCriteria = new DefaultPageCriteria(0,MAXEXPIREDRESULTS); + return getOrphanedAuthenticationHolders(pageCriteria); } + @Override + @Transactional(value="defaultTransactionManager") + public List getOrphanedAuthenticationHolders(PageCriteria pageCriteria) { + TypedQuery query = manager.createNamedQuery(AuthenticationHolderEntity.QUERY_GET_UNUSED, AuthenticationHolderEntity.class); + return JpaUtil.getResultPage(query, pageCriteria); + } + } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthorizationCodeRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthorizationCodeRepository.java index 59623c9e11..ffaef90215 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthorizationCodeRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthorizationCodeRepository.java @@ -26,6 +26,7 @@ import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; +import org.mitre.data.PageCriteria; import org.mitre.oauth2.model.AuthorizationCodeEntity; import org.mitre.oauth2.repository.AuthorizationCodeRepository; import org.mitre.util.jpa.JpaUtil; @@ -91,5 +92,13 @@ public Collection getExpiredCodes() { } + @Override + public Collection getExpiredCodes(PageCriteria pageCriteria) { + TypedQuery query = manager.createNamedQuery(AuthorizationCodeEntity.QUERY_EXPIRATION_BY_DATE, AuthorizationCodeEntity.class); + query.setParameter(AuthorizationCodeEntity.PARAM_DATE, new Date()); // this gets anything that's already expired + return JpaUtil.getResultPage(query, pageCriteria); + } + + } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java index d10480e16d..c851d2ee9f 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java @@ -31,6 +31,8 @@ import javax.persistence.criteria.CriteriaDelete; import javax.persistence.criteria.Root; +import org.mitre.data.DefaultPageCriteria; +import org.mitre.data.PageCriteria; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.oauth2.model.OAuth2RefreshTokenEntity; @@ -189,20 +191,32 @@ public List getRefreshTokensForClient(ClientDetailsEnt @Override public Set getAllExpiredAccessTokens() { - TypedQuery query = manager.createNamedQuery(OAuth2AccessTokenEntity.QUERY_EXPIRED_BY_DATE, OAuth2AccessTokenEntity.class); - query.setParameter(OAuth2AccessTokenEntity.PARAM_DATE, new Date()); - query.setMaxResults(MAXEXPIREDRESULTS); - return new LinkedHashSet<>(query.getResultList()); + DefaultPageCriteria pageCriteria = new DefaultPageCriteria(0, MAXEXPIREDRESULTS); + return getAllExpiredAccessTokens(pageCriteria); } + @Override + public Set getAllExpiredAccessTokens(PageCriteria pageCriteria) { + TypedQuery query = manager.createNamedQuery(OAuth2AccessTokenEntity.QUERY_EXPIRED_BY_DATE, OAuth2AccessTokenEntity.class); + query.setParameter(OAuth2AccessTokenEntity.PARAM_DATE, new Date()); + return new LinkedHashSet<>(JpaUtil.getResultPage(query, pageCriteria)); + } + @Override public Set getAllExpiredRefreshTokens() { - TypedQuery query = manager.createNamedQuery(OAuth2RefreshTokenEntity.QUERY_EXPIRED_BY_DATE, OAuth2RefreshTokenEntity.class); - query.setParameter(OAuth2RefreshTokenEntity.PARAM_DATE, new Date()); - query.setMaxResults(MAXEXPIREDRESULTS); - return new LinkedHashSet<>(query.getResultList()); + DefaultPageCriteria pageCriteria = new DefaultPageCriteria(0, MAXEXPIREDRESULTS); + return getAllExpiredRefreshTokens(pageCriteria); } + @Override + public Set getAllExpiredRefreshTokens(PageCriteria pageCriteria) { + TypedQuery query = manager.createNamedQuery(OAuth2RefreshTokenEntity.QUERY_EXPIRED_BY_DATE, OAuth2RefreshTokenEntity.class); + query.setParameter(OAuth2AccessTokenEntity.PARAM_DATE, new Date()); + return new LinkedHashSet<>(JpaUtil.getResultPage(query,pageCriteria)); + } + + + /* (non-Javadoc) * @see org.mitre.oauth2.repository.OAuth2TokenRepository#getAccessTokensForResourceSet(org.mitre.uma.model.ResourceSet) */ diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java index d6445d1bc1..5312f52ff7 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.Date; +import org.mitre.data.AbstractPageOperationTemplate; import org.mitre.oauth2.model.AuthenticationHolderEntity; import org.mitre.oauth2.model.AuthorizationCodeEntity; import org.mitre.oauth2.repository.AuthenticationHolderRepository; @@ -116,15 +117,17 @@ public OAuth2Authentication consumeAuthorizationCode(String code) throws Invalid @Transactional(value="defaultTransactionManager") public void clearExpiredAuthorizationCodes() { - Collection codes = repository.getExpiredCodes(); - - for (AuthorizationCodeEntity code : codes) { - repository.remove(code); - } - - if (codes.size() > 0) { - logger.info("Removed " + codes.size() + " expired authorization codes."); - } + new AbstractPageOperationTemplate(){ + @Override + public Collection fetchPage() { + return repository.getExpiredCodes(); + } + + @Override + protected void doOperation(AuthorizationCodeEntity item) { + repository.remove(item); + } + }.execute(); } /** diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java index 8bfcd970b4..a4d72c4fc2 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java @@ -33,6 +33,8 @@ import java.util.Set; import java.util.UUID; +import org.mitre.data.AbstractPageOperationTemplate; +import org.mitre.data.DefaultPageCriteria; import org.mitre.oauth2.model.AuthenticationHolderEntity; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.OAuth2AccessTokenEntity; @@ -490,47 +492,41 @@ public List getRefreshTokensForClient(ClientDetailsEnt public void clearExpiredTokens() { logger.debug("Cleaning out all expired tokens"); - Collection accessTokens = getExpiredAccessTokens(); - if (accessTokens.size() > 0) { - logger.info("Found " + accessTokens.size() + " expired access tokens"); - } - for (OAuth2AccessTokenEntity oAuth2AccessTokenEntity : accessTokens) { - try { - revokeAccessToken(oAuth2AccessTokenEntity); - } catch (IllegalArgumentException e) { - //An ID token is deleted with its corresponding access token, but then the ID token is on the list of expired tokens as well and there is - //nothing in place to distinguish it from any other. - //An attempt to delete an already deleted token returns an error, stopping the cleanup dead. We need it to keep going. - } - } - - Collection refreshTokens = getExpiredRefreshTokens(); - if (refreshTokens.size() > 0) { - logger.info("Found " + refreshTokens.size() + " expired refresh tokens"); - } - for (OAuth2RefreshTokenEntity oAuth2RefreshTokenEntity : refreshTokens) { - revokeRefreshToken(oAuth2RefreshTokenEntity); - } - - Collection authHolders = getOrphanedAuthenticationHolders(); - if (authHolders.size() > 0) { - logger.info("Found " + authHolders.size() + " orphaned authentication holders"); - } - for(AuthenticationHolderEntity authHolder : authHolders) { - authenticationHolderRepository.remove(authHolder); - } - } - - private Collection getExpiredAccessTokens() { - return Sets.newHashSet(tokenRepository.getAllExpiredAccessTokens()); - } - - private Collection getExpiredRefreshTokens() { - return Sets.newHashSet(tokenRepository.getAllExpiredRefreshTokens()); - } - - private Collection getOrphanedAuthenticationHolders() { - return Sets.newHashSet(authenticationHolderRepository.getOrphanedAuthenticationHolders()); + new AbstractPageOperationTemplate() { + @Override + public Collection fetchPage() { + return tokenRepository.getAllExpiredAccessTokens(new DefaultPageCriteria()); + } + + @Override + public void doOperation(OAuth2AccessTokenEntity item) { + revokeAccessToken(item); + } + }.execute(); + + new AbstractPageOperationTemplate() { + @Override + public Collection fetchPage() { + return tokenRepository.getAllExpiredRefreshTokens(new DefaultPageCriteria()); + } + + @Override + public void doOperation(OAuth2RefreshTokenEntity item) { + revokeRefreshToken(item); + } + }.execute(); + + new AbstractPageOperationTemplate() { + @Override + public Collection fetchPage() { + return authenticationHolderRepository.getOrphanedAuthenticationHolders(new DefaultPageCriteria()); + } + + @Override + public void doOperation(AuthenticationHolderEntity item) { + authenticationHolderRepository.remove(item); + } + }.execute(); } /* (non-Javadoc) From bdaf7cba23bb57265fad65ace38841fd779cf606 Mon Sep 17 00:00:00 2001 From: strangeweaver Date: Thu, 18 Feb 2016 13:21:39 +0000 Subject: [PATCH 109/269] Fixed race condition in test and added timeouts to prevent possibility of infinite loop. --- .../AbstractPageOperationTemplateTest.java | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java b/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java index 1aaa46869c..6155266e54 100644 --- a/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java +++ b/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java @@ -19,7 +19,7 @@ public class AbstractPageOperationTemplateTest { public void setUp() throws Exception { } - @Test + @Test(timeout = 1000L) public void execute_zeropages() { CountingPageOperation op = new CountingPageOperation(0,Long.MAX_VALUE); op.execute(); @@ -27,7 +27,7 @@ public void execute_zeropages() { assertEquals(0L, op.counter); } - @Test + @Test(timeout = 1000L) public void execute_singlepage() { CountingPageOperation op = new CountingPageOperation(1,Long.MAX_VALUE); op.execute(); @@ -35,7 +35,7 @@ public void execute_singlepage() { assertEquals(10L, op.counter); } - @Test + @Test(timeout = 1000L) public void execute_negpage() { CountingPageOperation op = new CountingPageOperation(-1,Long.MAX_VALUE); op.execute(); @@ -43,7 +43,7 @@ public void execute_negpage() { assertEquals(0L, op.counter); } - @Test + @Test(timeout = 1000L) public void execute_npage(){ int n = 7; CountingPageOperation op = new CountingPageOperation(n,Long.MAX_VALUE); @@ -52,7 +52,7 @@ public void execute_npage(){ assertEquals(n*10L, op.counter); } - @Test + @Test(timeout = 1000L) public void execute_nullpage(){ CountingPageOperation op = new NullPageCountingPageOperation(Integer.MAX_VALUE, Long.MAX_VALUE); op.execute(); @@ -60,7 +60,7 @@ public void execute_nullpage(){ assertEquals(0L, op.getCounter()); } - @Test + @Test(timeout = 1000L) public void execute_emptypage(){ CountingPageOperation op = new EmptyPageCountingPageOperation(Integer.MAX_VALUE, Long.MAX_VALUE); op.execute(); @@ -68,27 +68,30 @@ public void execute_emptypage(){ assertEquals(0L, op.getCounter()); } - @Test + @Test(timeout = 1000L) public void execute_zerotime(){ CountingPageOperation op = new CountingPageOperation(Integer.MAX_VALUE,0L); op.execute(); assertEquals(0L, op.getCounter()); - assertEquals(0L, op.getLastFetchTime()); + assertEquals(0L, op.getLastFetchRequestTime()); } - @Test + @Test(timeout = 1000L) public void execute_nonzerotime(){ - Long timeMillis = 100L; + Long timeMillis = 200L; CountingPageOperation op = new CountingPageOperation(Integer.MAX_VALUE,timeMillis); op.execute(); assertTrue("start time " + op.getStartTime() + "" + - " to last fetch time " + op.getLastFetchTime() + - " exceeds max time" + timeMillis, op.getLastFetchTime() - op.getStartTime() <= timeMillis); + " to last fetch time " + op.getLastFetchRequestTime() + + " or previous fetch time " + op.getPreviousFetchRequestTime() + + " exceeds max time" + timeMillis + , (op.getLastFetchRequestTime() - op.getStartTime() <= timeMillis) + || (op.getPreviousFetchRequestTime() - op.getStartTime() <= timeMillis)); } - @Test + @Test(timeout = 1000L) public void execute_negtime(){ Long timeMillis = -100L; CountingPageOperation op = new CountingPageOperation(Integer.MAX_VALUE,timeMillis); @@ -97,7 +100,7 @@ public void execute_negtime(){ assertEquals(0L, op.getCounter()); } - @Test + @Test(timeout = 1000L) public void execute_swallowException(){ CountingPageOperation op = new EvenExceptionCountingPageOperation(1, 1000L); op.execute(); @@ -125,7 +128,8 @@ private static class CountingPageOperation extends AbstractPageOperationTemplate private int pageSize = 10; private long counter = 0L; private long startTime; - private long lastFetchTime; + private long lastFetchRequestTime; + private long previousFetchRequestTime; private CountingPageOperation(int maxPages, long maxTime) { super(maxPages, maxTime); @@ -134,8 +138,12 @@ private CountingPageOperation(int maxPages, long maxTime) { @Override public Collection fetchPage() { - lastFetchTime = System.currentTimeMillis(); - List page = new ArrayList<>(pageSize); + if(lastFetchRequestTime > 0){ + previousFetchRequestTime = lastFetchRequestTime; + } + + lastFetchRequestTime = System.currentTimeMillis(); + List page = new ArrayList(pageSize); for(int i = 0; i < pageSize; i++ ) { page.add("item " + currentPageFetch * pageSize + i); } @@ -152,8 +160,12 @@ public long getCounter() { return counter; } - public long getLastFetchTime() { - return lastFetchTime; + public long getLastFetchRequestTime() { + return lastFetchRequestTime; + } + + public long getPreviousFetchRequestTime() { + return previousFetchRequestTime; } public long getStartTime(){ From 524794fe2e041696b6c3c155c406e8fe097c1c6d Mon Sep 17 00:00:00 2001 From: strangeweaver Date: Thu, 18 Feb 2016 14:05:47 +0000 Subject: [PATCH 110/269] Ignore timeout test as it's vulnerable to a race condition. --- .../AbstractPageOperationTemplateTest.java | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java b/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java index 6155266e54..6dcc8a2bfa 100644 --- a/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java +++ b/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java @@ -1,6 +1,7 @@ package org.mitre.data; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.util.ArrayList; @@ -8,6 +9,7 @@ import java.util.List; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; /** @@ -74,21 +76,25 @@ public void execute_zerotime(){ op.execute(); assertEquals(0L, op.getCounter()); - assertEquals(0L, op.getLastFetchRequestTime()); + assertEquals(0L, op.getTimeToLastFetch()); } + /* + * This is a valid test however it is vulnerable to a race condition + * as such it is being ignored. + */ @Test(timeout = 1000L) + @Ignore public void execute_nonzerotime(){ Long timeMillis = 200L; CountingPageOperation op = new CountingPageOperation(Integer.MAX_VALUE,timeMillis); op.execute(); - assertTrue("start time " + op.getStartTime() + "" + - " to last fetch time " + op.getLastFetchRequestTime() + - " or previous fetch time " + op.getPreviousFetchRequestTime() + - " exceeds max time" + timeMillis - , (op.getLastFetchRequestTime() - op.getStartTime() <= timeMillis) - || (op.getPreviousFetchRequestTime() - op.getStartTime() <= timeMillis)); + assertFalse("last fetch time " + op.getTimeToLastFetch() + "" + + " and previous fetch time " + op.getTimeToPreviousFetch() + + " exceed max time" + timeMillis, + op.getTimeToLastFetch() > timeMillis + && op.getTimeToPreviousFetch() > timeMillis); } @Test(timeout = 1000L) @@ -128,8 +134,8 @@ private static class CountingPageOperation extends AbstractPageOperationTemplate private int pageSize = 10; private long counter = 0L; private long startTime; - private long lastFetchRequestTime; - private long previousFetchRequestTime; + private long timeToLastFetch; + private long timeToPreviousFetch; private CountingPageOperation(int maxPages, long maxTime) { super(maxPages, maxTime); @@ -138,11 +144,9 @@ private CountingPageOperation(int maxPages, long maxTime) { @Override public Collection fetchPage() { - if(lastFetchRequestTime > 0){ - previousFetchRequestTime = lastFetchRequestTime; - } + timeToPreviousFetch = timeToLastFetch > 0 ? timeToLastFetch : 0; + timeToLastFetch = System.currentTimeMillis() - startTime; - lastFetchRequestTime = System.currentTimeMillis(); List page = new ArrayList(pageSize); for(int i = 0; i < pageSize; i++ ) { page.add("item " + currentPageFetch * pageSize + i); @@ -160,12 +164,12 @@ public long getCounter() { return counter; } - public long getLastFetchRequestTime() { - return lastFetchRequestTime; + public long getTimeToLastFetch() { + return timeToLastFetch; } - public long getPreviousFetchRequestTime() { - return previousFetchRequestTime; + public long getTimeToPreviousFetch() { + return timeToPreviousFetch; } public long getStartTime(){ From b17a7f43ae9a94592132a0336248631fe5914508 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 17 Jan 2017 17:06:04 -0500 Subject: [PATCH 111/269] removed structured scopes --- .../org/mitre/oauth2/model/SystemScope.java | 79 +------------------ .../oauth2/service/SystemScopeService.java | 7 +- .../db/hsql/hsql_database_tables.sql | 5 +- .../resources/db/hsql/loading_temp_tables.sql | 4 +- .../src/main/resources/db/hsql/scopes.sql | 18 ++--- .../src/main/webapp/WEB-INF/authz-config.xml | 2 +- .../src/main/webapp/WEB-INF/views/approve.jsp | 4 - .../resources/js/locale/en/messages.json | 3 - .../src/main/webapp/resources/js/scope.js | 21 +---- .../main/webapp/resources/template/scope.html | 13 --- .../impl/DefaultSystemScopeService.java | 41 +--------- ...peServiceAwareOAuth2RequestValidator.java} | 4 +- .../service/impl/MITREidDataService_1_1.java | 4 +- .../service/impl/MITREidDataService_1_2.java | 4 +- .../service/impl/MITREidDataService_1_3.java | 8 -- .../token/TofuUserApprovalHandler.java | 10 +-- .../impl/TestDefaultSystemScopeService.java | 54 ++----------- .../impl/TestMITREidDataService_1_1.java | 10 +-- .../impl/TestMITREidDataService_1_2.java | 10 +-- .../impl/TestMITREidDataService_1_3.java | 10 +-- .../service/impl/MITREidDataService_1_2.java | 6 +- 21 files changed, 39 insertions(+), 278 deletions(-) rename openid-connect-server/src/main/java/org/mitre/oauth2/token/{StructuredScopeAwareOAuth2RequestValidator.java => ScopeServiceAwareOAuth2RequestValidator.java} (93%) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/SystemScope.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/SystemScope.java index 19e6bcec1a..f559ab4d1b 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/SystemScope.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/SystemScope.java @@ -53,9 +53,6 @@ public class SystemScope { private String icon; // class of the icon to display on the auth page private boolean defaultScope = false; // is this a default scope for newly-registered clients? private boolean restricted = false; // is this scope restricted to admin-only registration access? - private boolean structured = false; // is this a default scope for newly-registered clients? - private String structuredParamDescription; - private String structuredValue; /** * Make a blank system scope with no value @@ -162,52 +159,6 @@ public void setRestricted(boolean restricted) { this.restricted = restricted; } - /** - * @return the isStructured status - */ - @Basic - @Column(name = "structured") - public boolean isStructured() { - return structured; - } - - /** - * @param structured the structured to set - */ - public void setStructured(boolean structured) { - this.structured = structured; - } - - @Basic - @Column(name = "structured_param_description") - public String getStructuredParamDescription() { - return structuredParamDescription; - } - - /** - * @param isStructured the isStructured to set - */ - public void setStructuredParamDescription(String d) { - this.structuredParamDescription = d; - } - - - /** - * @return the structuredValue - */ - @Transient // we don't save the value of a system scope separately - public String getStructuredValue() { - return structuredValue; - } - - /** - * @param structuredValue the structuredValue to set - */ - public void setStructuredValue(String structuredValue) { - this.structuredValue = structuredValue; - } - - /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @@ -221,13 +172,6 @@ public int hashCode() { result = prime * result + ((icon == null) ? 0 : icon.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + (restricted ? 1231 : 1237); - result = prime * result + (structured ? 1231 : 1237); - result = prime - * result - + ((structuredParamDescription == null) ? 0 - : structuredParamDescription.hashCode()); - result = prime * result - + ((structuredValue == null) ? 0 : structuredValue.hashCode()); result = prime * result + ((value == null) ? 0 : value.hashCode()); return result; } @@ -274,24 +218,6 @@ public boolean equals(Object obj) { if (restricted != other.restricted) { return false; } - if (structured != other.structured) { - return false; - } - if (structuredParamDescription == null) { - if (other.structuredParamDescription != null) { - return false; - } - } else if (!structuredParamDescription - .equals(other.structuredParamDescription)) { - return false; - } - if (structuredValue == null) { - if (other.structuredValue != null) { - return false; - } - } else if (!structuredValue.equals(other.structuredValue)) { - return false; - } if (value == null) { if (other.value != null) { return false; @@ -309,10 +235,7 @@ public boolean equals(Object obj) { public String toString() { return "SystemScope [id=" + id + ", value=" + value + ", description=" + description + ", icon=" + icon + ", defaultScope=" - + defaultScope + ", restricted=" + restricted + ", structured=" - + structured + ", structuredParamDescription=" - + structuredParamDescription + ", structuredValue=" - + structuredValue + "]"; + + defaultScope + ", restricted=" + restricted + "]"; } } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/SystemScopeService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/SystemScopeService.java index 1b4298634b..72f171e098 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/SystemScopeService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/SystemScopeService.java @@ -96,12 +96,7 @@ public interface SystemScopeService { public Set toStrings(Set scope); /** - * Test whether the scopes in both sets are compatible, with special - * processing for structured scopes. All scopes in "actual" must exist in - * "expected". If a scope in "expected" is structured and has a value, it - * must be matched exactly by its corresponding scope in "actual". If a - * scope in "expected" is structured but has no value, it may be matched by - * a scope with or without a value in "actual". + * Test whether the scopes in both sets are compatible. All scopes in "actual" must exist in "expected". */ public boolean scopesMatch(Set expected, Set actual); diff --git a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql index 90bea3092c..23493f0eae 100644 --- a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql @@ -10,7 +10,8 @@ CREATE TABLE IF NOT EXISTS access_token ( refresh_token_id BIGINT, client_id BIGINT, auth_holder_id BIGINT, - approved_site_id BIGINT + approved_site_id BIGINT, + UNIQUE(token_value) ); CREATE TABLE IF NOT EXISTS access_token_permissions ( @@ -235,8 +236,6 @@ CREATE TABLE IF NOT EXISTS system_scope ( icon VARCHAR(256), restricted BOOLEAN DEFAULT false NOT NULL, default_scope BOOLEAN DEFAULT false NOT NULL, - structured BOOLEAN DEFAULT false NOT NULL, - structured_param_description VARCHAR(256), UNIQUE (scope) ); diff --git a/openid-connect-server-webapp/src/main/resources/db/hsql/loading_temp_tables.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/loading_temp_tables.sql index 1d3908ed11..37b0092e75 100644 --- a/openid-connect-server-webapp/src/main/resources/db/hsql/loading_temp_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/hsql/loading_temp_tables.sql @@ -69,7 +69,5 @@ CREATE TEMPORARY TABLE IF NOT EXISTS system_scope_TEMP ( description VARCHAR(4096), icon VARCHAR(256), restricted BOOLEAN, - default_scope BOOLEAN, - structured BOOLEAN, - structured_param_description VARCHAR(256) + default_scope BOOLEAN ); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/resources/db/hsql/scopes.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/scopes.sql index 27792880fc..8e72c88c7f 100644 --- a/openid-connect-server-webapp/src/main/resources/db/hsql/scopes.sql +++ b/openid-connect-server-webapp/src/main/resources/db/hsql/scopes.sql @@ -10,23 +10,23 @@ START TRANSACTION; -- Insert scope information into the temporary tables. -- -INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES - ('openid', 'log in using your identity', 'user', false, true, false, null), - ('profile', 'basic profile information', 'list-alt', false, true, false, null), - ('email', 'email address', 'envelope', false, true, false, null), - ('address', 'physical address', 'home', false, true, false, null), - ('phone', 'telephone number', 'bell', false, true, false, null), - ('offline_access', 'offline access', 'time', false, false, false, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope) VALUES + ('openid', 'log in using your identity', 'user', false, true), + ('profile', 'basic profile information', 'list-alt', false, true), + ('email', 'email address', 'envelope', false, true), + ('address', 'physical address', 'home', false, true), + ('phone', 'telephone number', 'bell', false, true), + ('offline_access', 'offline access', 'time', false, false); -- -- Merge the temporary scopes safely into the database. This is a two-step process to keep scopes from being created on every startup with a persistent store. -- MERGE INTO system_scope - USING (SELECT scope, description, icon, restricted, default_scope, structured, structured_param_description FROM system_scope_TEMP) AS vals(scope, description, icon, restricted, default_scope, structured, structured_param_description) + USING (SELECT scope, description, icon, restricted, default_scope FROM system_scope_TEMP) AS vals(scope, description, icon, restricted, default_scope) ON vals.scope = system_scope.scope WHEN NOT MATCHED THEN - INSERT (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES(vals.scope, vals.description, vals.icon, vals.restricted, vals.default_scope, vals.structured, vals.structured_param_description); + INSERT (scope, description, icon, restricted, default_scope) VALUES(vals.scope, vals.description, vals.icon, vals.restricted, vals.default_scope); COMMIT; diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml index 9c72fbbc5e..a87e4ddb83 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml @@ -52,7 +52,7 @@ - + diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approve.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approve.jsp index 1ef03568f2..5624208c45 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approve.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approve.jsp @@ -218,10 +218,6 @@ - - - - diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json index f787e0d544..0722b93a25 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json @@ -306,9 +306,6 @@ "icon": "Icon", "new": "New Scope", "select-icon": "Select an icon", - "structured": "is a structured scope", - "structured-help": "Is the scope structured with structured values like base:extension?", - "structured-param-help": "Human-readable description of the structured parameter", "subject-type": "Subject Type", "value": "Scope value", "value-help": "Single string with no spaces", diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js b/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js index 496155b03d..b2d03f8ef0 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js @@ -23,10 +23,7 @@ var SystemScopeModel = Backbone.Model.extend({ icon:null, value:null, defaultScope:false, - restricted:false, - structured:false, - structuredParamDescription:null, - structuredValue:null + restricted:false }, urlRoot: 'api/scopes' @@ -267,8 +264,7 @@ var SystemScopeFormView = Backbone.View.extend({ events:{ 'click .btn-save':'saveScope', 'click .btn-cancel': function() {app.navigate('admin/scope', {trigger: true}); }, - 'click .btn-icon':'selectIcon', - 'change #isStructured input':'toggleStructuredParamDescription' + 'click .btn-icon':'selectIcon' }, load:function(callback) { @@ -290,14 +286,6 @@ var SystemScopeFormView = Backbone.View.extend({ }, - toggleStructuredParamDescription:function(e) { - if ($('#isStructured input', this.el).is(':checked')) { - $('#structuredParamDescription', this.el).show(); - } else { - $('#structuredParamDescription', this.el).hide(); - } - }, - saveScope:function(e) { e.preventDefault(); @@ -313,9 +301,7 @@ var SystemScopeFormView = Backbone.View.extend({ description:$('#description textarea').val(), icon:$('#iconDisplay input').val(), defaultScope:$('#defaultScope input').is(':checked'), - restricted:$('#restricted input').is(':checked'), - structured:$('#isStructured input').is(':checked'), - structuredParamDescription:$('#structuredParamDescription input').val() + restricted:$('#restricted input').is(':checked') }); if (valid) { @@ -356,7 +342,6 @@ var SystemScopeFormView = Backbone.View.extend({ $("#iconSelector .modal-body", this.el).append(this.iconTemplate({items:items})); }, this); - this.toggleStructuredParamDescription(); $(this.el).i18n(); return this; } diff --git a/openid-connect-server-webapp/src/main/webapp/resources/template/scope.html b/openid-connect-server-webapp/src/main/webapp/resources/template/scope.html index e210bedaaa..766dc4323c 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/template/scope.html +++ b/openid-connect-server-webapp/src/main/webapp/resources/template/scope.html @@ -155,19 +155,6 @@

Selec

-
-
- -

Is the scope structured with structured values like base:extension?

-
-
- -

Human-readable description of the structured parameter

-
-
-
  diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultSystemScopeService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultSystemScopeService.java index 3efd10a21a..62734f34ca 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultSystemScopeService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultSystemScopeService.java @@ -76,20 +76,11 @@ public SystemScope apply(String input) { if (Strings.isNullOrEmpty(input)) { return null; } else { - List parts = parseStructuredScopeValue(input); - String base = parts.get(0); // the first part is the base // get the real scope if it's available - SystemScope s = getByValue(base); + SystemScope s = getByValue(input); if (s == null) { // make a fake one otherwise - s = new SystemScope(base); - if (parts.size() > 1) { - s.setStructured(true); - } - } - - if (s.isStructured() && parts.size() > 1) { - s.setStructuredValue(parts.get(1)); + s = new SystemScope(input); } return s; @@ -103,11 +94,7 @@ public String apply(SystemScope input) { if (input == null) { return null; } else { - if (input.isStructured() && !Strings.isNullOrEmpty(input.getStructuredValue())) { - return Joiner.on(":").join(input.getValue(), input.getStructuredValue()); - } else { - return input.getValue(); - } + return input.getValue(); } } }; @@ -181,11 +168,6 @@ public Set toStrings(Set scope) { } } - // parse a structured scope string into its components - private List parseStructuredScopeValue(String value) { - return Lists.newArrayList(Splitter.on(":").split(value)); - } - /* (non-Javadoc) * @see org.mitre.oauth2.service.SystemScopeService#scopesMatch(java.util.Set, java.util.Set) */ @@ -198,22 +180,7 @@ public boolean scopesMatch(Set expected, Set actual) { for (SystemScope actScope : act) { // first check to see if there's an exact match if (!ex.contains(actScope)) { - // we didn't find an exact match - if (actScope.isStructured() && !Strings.isNullOrEmpty(actScope.getStructuredValue())) { - // if we didn't get an exact match but the actual scope is structured, we need to check further - - // first, find the "base" scope for this - SystemScope base = getByValue(actScope.getValue()); - if (!ex.contains(base)) { - // if the expected doesn't contain the base scope, fail - return false; - } else { - // we did find an exact match, need to check the rest - } - } else { - // the scope wasn't structured, fail now - return false; - } + return false; } else { // if we did find an exact match, we need to check the rest } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/token/StructuredScopeAwareOAuth2RequestValidator.java b/openid-connect-server/src/main/java/org/mitre/oauth2/token/ScopeServiceAwareOAuth2RequestValidator.java similarity index 93% rename from openid-connect-server/src/main/java/org/mitre/oauth2/token/StructuredScopeAwareOAuth2RequestValidator.java rename to openid-connect-server/src/main/java/org/mitre/oauth2/token/ScopeServiceAwareOAuth2RequestValidator.java index b08fda4e73..af4028ad4f 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/token/StructuredScopeAwareOAuth2RequestValidator.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/token/ScopeServiceAwareOAuth2RequestValidator.java @@ -32,12 +32,12 @@ /** * * Validates the scopes on a request by comparing them against a client's - * allowed scopes, but allow structured scopes to function. + * allowed scopes, but allow custom scopes to function through the system scopes * * @author jricher * */ -public class StructuredScopeAwareOAuth2RequestValidator implements OAuth2RequestValidator { +public class ScopeServiceAwareOAuth2RequestValidator implements OAuth2RequestValidator { @Autowired private SystemScopeService scopeService; diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java index 7955ded756..2372943240 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java @@ -817,9 +817,9 @@ private void readSystemScopes(JsonReader reader) throws IOException { } else if (name.equals("defaultScope")) { scope.setDefaultScope(reader.nextBoolean()); } else if (name.equals("structured")) { - scope.setStructured(reader.nextBoolean()); + logger.warn("Found a structured scope, ignoring structure"); } else if (name.equals("structuredParameter")) { - scope.setStructuredParamDescription(reader.nextString()); + logger.warn("Found a structured scope, ignoring structure"); } else if (name.equals("icon")) { scope.setIcon(reader.nextString()); } else { diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java index da28b5d8f7..7b0becc76f 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java @@ -808,9 +808,9 @@ private void readSystemScopes(JsonReader reader) throws IOException { } else if (name.equals(ICON)) { scope.setIcon(reader.nextString()); } else if (name.equals(STRUCTURED)) { - scope.setStructured(reader.nextBoolean()); + logger.warn("Found a structured scope, ignoring structure"); } else if (name.equals(STRUCTURED_PARAMETER)) { - scope.setStructuredParamDescription(reader.nextString()); + logger.warn("Found a structured scope, ignoring structure"); } else { logger.debug("found unexpected entry"); reader.skipValue(); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java index ed0503110b..e465175f45 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java @@ -80,8 +80,6 @@ public class MITREidDataService_1_3 extends MITREidDataServiceSupport implements MITREidDataService { private static final String DEFAULT_SCOPE = "defaultScope"; - private static final String STRUCTURED_PARAMETER = "structuredParameter"; - private static final String STRUCTURED = "structured"; private static final String RESTRICTED = "restricted"; private static final String ICON = "icon"; private static final String DYNAMICALLY_REGISTERED = "dynamicallyRegistered"; @@ -525,8 +523,6 @@ private void writeSystemScopes(JsonWriter writer) { writer.name(ICON).value(sysScope.getIcon()); writer.name(VALUE).value(sysScope.getValue()); writer.name(RESTRICTED).value(sysScope.isRestricted()); - writer.name(STRUCTURED).value(sysScope.isStructured()); - writer.name(STRUCTURED_PARAMETER).value(sysScope.getStructuredParamDescription()); writer.name(DEFAULT_SCOPE).value(sysScope.isDefaultScope()); writer.endObject(); logger.debug("Wrote system scope {}", sysScope.getId()); @@ -1180,10 +1176,6 @@ private void readSystemScopes(JsonReader reader) throws IOException { scope.setDefaultScope(reader.nextBoolean()); } else if (name.equals(ICON)) { scope.setIcon(reader.nextString()); - } else if (name.equals(STRUCTURED)) { - scope.setStructured(reader.nextBoolean()); - } else if (name.equals(STRUCTURED_PARAMETER)) { - scope.setStructuredParamDescription(reader.nextString()); } else { logger.debug("found unexpected entry"); reader.skipValue(); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/TofuUserApprovalHandler.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/TofuUserApprovalHandler.java index 91489ba1c9..f1319c07cb 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/TofuUserApprovalHandler.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/TofuUserApprovalHandler.java @@ -204,15 +204,7 @@ public AuthorizationRequest updateAfterApproval(AuthorizationRequest authorizati //Make sure this scope is allowed for the given client if (systemScopes.scopesMatch(client.getScope(), approveSet)) { - // If it's structured, assign the user-specified parameter - SystemScope systemScope = systemScopes.getByValue(scope); - if (systemScope != null && systemScope.isStructured()){ - String paramValue = approvalParams.get("scopeparam_" + scope); - allowedScopes.add(scope + ":"+paramValue); - // .. and if it's unstructured, we're all set - } else { - allowedScopes.add(scope); - } + allowedScopes.add(scope); } } diff --git a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultSystemScopeService.java b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultSystemScopeService.java index b68082db19..f1c6d6936a 100644 --- a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultSystemScopeService.java +++ b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultSystemScopeService.java @@ -52,8 +52,6 @@ public class TestDefaultSystemScopeService { private SystemScope defaultScope2; private SystemScope dynScope1; private SystemScope restrictedScope1; - private SystemScope structuredScope1; - private SystemScope structuredScope1Value; private String defaultDynScope1String = "defaultDynScope1"; private String defaultDynScope2String = "defaultDynScope2"; @@ -61,8 +59,6 @@ public class TestDefaultSystemScopeService { private String defaultScope2String = "defaultScope2"; private String dynScope1String = "dynScope1"; private String restrictedScope1String = "restrictedScope1"; - private String structuredScope1String = "structuredScope1"; - private String structuredValue = "structuredValue"; private Set allScopes; private Set allScopeStrings; @@ -105,20 +101,11 @@ public void prepare() { restrictedScope1.setRestricted(true); - // structuredScope1 : structured scope - structuredScope1 = new SystemScope(structuredScope1String); - structuredScope1.setStructured(true); + allScopes = Sets.newHashSet(defaultDynScope1, defaultDynScope2, defaultScope1, defaultScope2, dynScope1, restrictedScope1); + allScopeStrings = Sets.newHashSet(defaultDynScope1String, defaultDynScope2String, defaultScope1String, defaultScope2String, dynScope1String, restrictedScope1String); - // structuredScope1Value : structured scope with value - structuredScope1Value = new SystemScope(structuredScope1String); - structuredScope1Value.setStructured(true); - structuredScope1Value.setStructuredValue(structuredValue); - - allScopes = Sets.newHashSet(defaultDynScope1, defaultDynScope2, defaultScope1, defaultScope2, dynScope1, restrictedScope1, structuredScope1); - allScopeStrings = Sets.newHashSet(defaultDynScope1String, defaultDynScope2String, defaultScope1String, defaultScope2String, dynScope1String, restrictedScope1String, structuredScope1String); - - allScopesWithValue = Sets.newHashSet(defaultDynScope1, defaultDynScope2, defaultScope1, defaultScope2, dynScope1, restrictedScope1, structuredScope1, structuredScope1Value); - allScopeStringsWithValue = Sets.newHashSet(defaultDynScope1String, defaultDynScope2String, defaultScope1String, defaultScope2String, dynScope1String, restrictedScope1String, structuredScope1String, structuredScope1String + ":" + structuredValue); + allScopesWithValue = Sets.newHashSet(defaultDynScope1, defaultDynScope2, defaultScope1, defaultScope2, dynScope1, restrictedScope1); + allScopeStringsWithValue = Sets.newHashSet(defaultDynScope1String, defaultDynScope2String, defaultScope1String, defaultScope2String, dynScope1String, restrictedScope1String); Mockito.when(repository.getByValue(defaultDynScope1String)).thenReturn(defaultDynScope1); Mockito.when(repository.getByValue(defaultDynScope2String)).thenReturn(defaultDynScope2); @@ -126,16 +113,6 @@ public void prepare() { Mockito.when(repository.getByValue(defaultScope2String)).thenReturn(defaultScope2); Mockito.when(repository.getByValue(dynScope1String)).thenReturn(dynScope1); Mockito.when(repository.getByValue(restrictedScope1String)).thenReturn(restrictedScope1); - // we re-use this value so we've got to use thenAnswer instead - Mockito.when(repository.getByValue(structuredScope1String)).thenAnswer(new Answer() { - @Override - public SystemScope answer(InvocationOnMock invocation) throws Throwable { - SystemScope s = new SystemScope(structuredScope1String); - s.setStructured(true); - return s; - } - - }); Mockito.when(repository.getAll()).thenReturn(allScopes); } @@ -157,7 +134,7 @@ public void getDefaults() { @Test public void getUnrestricted() { - Set unrestricted = Sets.newHashSet(defaultDynScope1, defaultDynScope2, dynScope1, structuredScope1); + Set unrestricted = Sets.newHashSet(defaultDynScope1, defaultDynScope2, dynScope1); assertThat(service.getUnrestricted(), equalTo(unrestricted)); } @@ -210,25 +187,4 @@ public void scopesMatch() { assertThat(service.scopesMatch(expected, actualBad), is(false)); } - @Test - public void scopesMatch_structured() { - Set expected = Sets.newHashSet("foo", "bar", "baz"); - Set actualGood = Sets.newHashSet("foo:value", "baz", "bar"); - Set actualBad = Sets.newHashSet("foo:value", "bar:value"); - - // note: we have to use "thenAnswer" here to mimic the repository not serializing the structuredValue field - Mockito.when(repository.getByValue("foo")).thenAnswer(new Answer() { - @Override - public SystemScope answer(InvocationOnMock invocation) throws Throwable { - SystemScope foo = new SystemScope("foo"); - foo.setStructured(true); - return foo; - } - - }); - - assertThat(service.scopesMatch(expected, actualGood), is(true)); - - assertThat(service.scopesMatch(expected, actualBad), is(false)); - } } diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java index cb2f589048..bcff4aa96b 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java @@ -782,8 +782,6 @@ public void testImportSystemScopes() throws IOException { scope3.setRestricted(false); scope3.setDefaultScope(true); scope3.setIcon("road"); - scope3.setStructured(true); - scope3.setStructuredParamDescription("Structured Parameter"); String configJson = "{" + "\"" + MITREidDataService.CLIENTS + "\": [], " + @@ -797,7 +795,7 @@ public void testImportSystemScopes() throws IOException { "{\"id\":1,\"description\":\"Scope 1\",\"icon\":\"glass\",\"value\":\"scope1\",\"allowDynReg\":false,\"defaultScope\":false}," + "{\"id\":2,\"description\":\"Scope 2\",\"icon\":\"ball\",\"value\":\"scope2\",\"allowDynReg\":true,\"defaultScope\":false}," + - "{\"id\":3,\"description\":\"Scope 3\",\"icon\":\"road\",\"value\":\"scope3\",\"allowDynReg\":true,\"defaultScope\":true,\"structured\":true,\"structuredParameter\":\"Structured Parameter\"}" + + "{\"id\":3,\"description\":\"Scope 3\",\"icon\":\"road\",\"value\":\"scope3\",\"allowDynReg\":true,\"defaultScope\":true}" + " ]" + "}"; @@ -817,24 +815,18 @@ public void testImportSystemScopes() throws IOException { assertThat(savedScopes.get(0).getIcon(), equalTo(scope1.getIcon())); assertThat(savedScopes.get(0).isDefaultScope(), equalTo(scope1.isDefaultScope())); assertThat(savedScopes.get(0).isRestricted(), equalTo(scope1.isRestricted())); - assertThat(savedScopes.get(0).isStructured(), equalTo(scope1.isStructured())); - assertThat(savedScopes.get(0).getStructuredParamDescription(), equalTo(scope1.getStructuredParamDescription())); assertThat(savedScopes.get(1).getValue(), equalTo(scope2.getValue())); assertThat(savedScopes.get(1).getDescription(), equalTo(scope2.getDescription())); assertThat(savedScopes.get(1).getIcon(), equalTo(scope2.getIcon())); assertThat(savedScopes.get(1).isDefaultScope(), equalTo(scope2.isDefaultScope())); assertThat(savedScopes.get(1).isRestricted(), equalTo(scope2.isRestricted())); - assertThat(savedScopes.get(1).isStructured(), equalTo(scope2.isStructured())); - assertThat(savedScopes.get(1).getStructuredParamDescription(), equalTo(scope2.getStructuredParamDescription())); assertThat(savedScopes.get(2).getValue(), equalTo(scope3.getValue())); assertThat(savedScopes.get(2).getDescription(), equalTo(scope3.getDescription())); assertThat(savedScopes.get(2).getIcon(), equalTo(scope3.getIcon())); assertThat(savedScopes.get(2).isDefaultScope(), equalTo(scope3.isDefaultScope())); assertThat(savedScopes.get(2).isRestricted(), equalTo(scope3.isRestricted())); - assertThat(savedScopes.get(2).isStructured(), equalTo(scope3.isStructured())); - assertThat(savedScopes.get(2).getStructuredParamDescription(), equalTo(scope3.getStructuredParamDescription())); } diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java index 44264eb33f..6f2832a7ff 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java @@ -797,8 +797,6 @@ public void testImportSystemScopes() throws IOException { scope3.setRestricted(false); scope3.setDefaultScope(true); scope3.setIcon("road"); - scope3.setStructured(true); - scope3.setStructuredParamDescription("Structured Parameter"); String configJson = "{" + "\"" + MITREidDataService.CLIENTS + "\": [], " + @@ -812,7 +810,7 @@ public void testImportSystemScopes() throws IOException { "{\"id\":1,\"description\":\"Scope 1\",\"icon\":\"glass\",\"value\":\"scope1\",\"restricted\":true,\"defaultScope\":false}," + "{\"id\":2,\"description\":\"Scope 2\",\"icon\":\"ball\",\"value\":\"scope2\",\"restricted\":false,\"defaultScope\":false}," + - "{\"id\":3,\"description\":\"Scope 3\",\"icon\":\"road\",\"value\":\"scope3\",\"restricted\":false,\"defaultScope\":true,\"structured\":true,\"structuredParameter\":\"Structured Parameter\"}" + + "{\"id\":3,\"description\":\"Scope 3\",\"icon\":\"road\",\"value\":\"scope3\",\"restricted\":false,\"defaultScope\":true}" + " ]" + "}"; @@ -832,24 +830,18 @@ public void testImportSystemScopes() throws IOException { assertThat(savedScopes.get(0).getIcon(), equalTo(scope1.getIcon())); assertThat(savedScopes.get(0).isDefaultScope(), equalTo(scope1.isDefaultScope())); assertThat(savedScopes.get(0).isRestricted(), equalTo(scope1.isRestricted())); - assertThat(savedScopes.get(0).isStructured(), equalTo(scope1.isStructured())); - assertThat(savedScopes.get(0).getStructuredParamDescription(), equalTo(scope1.getStructuredParamDescription())); assertThat(savedScopes.get(1).getValue(), equalTo(scope2.getValue())); assertThat(savedScopes.get(1).getDescription(), equalTo(scope2.getDescription())); assertThat(savedScopes.get(1).getIcon(), equalTo(scope2.getIcon())); assertThat(savedScopes.get(1).isDefaultScope(), equalTo(scope2.isDefaultScope())); assertThat(savedScopes.get(1).isRestricted(), equalTo(scope2.isRestricted())); - assertThat(savedScopes.get(1).isStructured(), equalTo(scope2.isStructured())); - assertThat(savedScopes.get(1).getStructuredParamDescription(), equalTo(scope2.getStructuredParamDescription())); assertThat(savedScopes.get(2).getValue(), equalTo(scope3.getValue())); assertThat(savedScopes.get(2).getDescription(), equalTo(scope3.getDescription())); assertThat(savedScopes.get(2).getIcon(), equalTo(scope3.getIcon())); assertThat(savedScopes.get(2).isDefaultScope(), equalTo(scope3.isDefaultScope())); assertThat(savedScopes.get(2).isRestricted(), equalTo(scope3.isRestricted())); - assertThat(savedScopes.get(2).isStructured(), equalTo(scope3.isStructured())); - assertThat(savedScopes.get(2).getStructuredParamDescription(), equalTo(scope3.getStructuredParamDescription())); } diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java index 4d2e71b720..d2cefc5941 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java @@ -1669,8 +1669,6 @@ public void testImportSystemScopes() throws IOException { scope3.setRestricted(false); scope3.setDefaultScope(true); scope3.setIcon("road"); - scope3.setStructured(true); - scope3.setStructuredParamDescription("Structured Parameter"); String configJson = "{" + "\"" + MITREidDataService.CLIENTS + "\": [], " + @@ -1684,7 +1682,7 @@ public void testImportSystemScopes() throws IOException { "{\"id\":1,\"description\":\"Scope 1\",\"icon\":\"glass\",\"value\":\"scope1\",\"restricted\":true,\"defaultScope\":false}," + "{\"id\":2,\"description\":\"Scope 2\",\"icon\":\"ball\",\"value\":\"scope2\",\"restricted\":false,\"defaultScope\":false}," + - "{\"id\":3,\"description\":\"Scope 3\",\"icon\":\"road\",\"value\":\"scope3\",\"restricted\":false,\"defaultScope\":true,\"structured\":true,\"structuredParameter\":\"Structured Parameter\"}" + + "{\"id\":3,\"description\":\"Scope 3\",\"icon\":\"road\",\"value\":\"scope3\",\"restricted\":false,\"defaultScope\":true}" + " ]" + "}"; @@ -1704,24 +1702,18 @@ public void testImportSystemScopes() throws IOException { assertThat(savedScopes.get(0).getIcon(), equalTo(scope1.getIcon())); assertThat(savedScopes.get(0).isDefaultScope(), equalTo(scope1.isDefaultScope())); assertThat(savedScopes.get(0).isRestricted(), equalTo(scope1.isRestricted())); - assertThat(savedScopes.get(0).isStructured(), equalTo(scope1.isStructured())); - assertThat(savedScopes.get(0).getStructuredParamDescription(), equalTo(scope1.getStructuredParamDescription())); assertThat(savedScopes.get(1).getValue(), equalTo(scope2.getValue())); assertThat(savedScopes.get(1).getDescription(), equalTo(scope2.getDescription())); assertThat(savedScopes.get(1).getIcon(), equalTo(scope2.getIcon())); assertThat(savedScopes.get(1).isDefaultScope(), equalTo(scope2.isDefaultScope())); assertThat(savedScopes.get(1).isRestricted(), equalTo(scope2.isRestricted())); - assertThat(savedScopes.get(1).isStructured(), equalTo(scope2.isStructured())); - assertThat(savedScopes.get(1).getStructuredParamDescription(), equalTo(scope2.getStructuredParamDescription())); assertThat(savedScopes.get(2).getValue(), equalTo(scope3.getValue())); assertThat(savedScopes.get(2).getDescription(), equalTo(scope3.getDescription())); assertThat(savedScopes.get(2).getIcon(), equalTo(scope3.getIcon())); assertThat(savedScopes.get(2).isDefaultScope(), equalTo(scope3.isDefaultScope())); assertThat(savedScopes.get(2).isRestricted(), equalTo(scope3.isRestricted())); - assertThat(savedScopes.get(2).isStructured(), equalTo(scope3.isStructured())); - assertThat(savedScopes.get(2).getStructuredParamDescription(), equalTo(scope3.getStructuredParamDescription())); } diff --git a/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java b/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java index 01cdb2f743..38295ba6be 100644 --- a/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java +++ b/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java @@ -726,8 +726,6 @@ private void writeSystemScopes(JsonWriter writer) { writer.name(ICON).value(sysScope.getIcon()); writer.name(VALUE).value(sysScope.getValue()); writer.name(RESTRICTED).value(sysScope.isRestricted()); - writer.name(STRUCTURED).value(sysScope.isStructured()); - writer.name(STRUCTURED_PARAMETER).value(sysScope.getStructuredParamDescription()); writer.name(DEFAULT_SCOPE).value(sysScope.isDefaultScope()); writer.endObject(); logger.debug("Wrote system scope {}", sysScope.getId()); @@ -1710,9 +1708,9 @@ private void readSystemScopes(JsonReader reader) throws IOException { } else if (name.equals(ICON)) { scope.setIcon(reader.nextString()); } else if (name.equals(STRUCTURED)) { - scope.setStructured(reader.nextBoolean()); + logger.debug("Found a structured scope, ignoring structure"); } else if (name.equals(STRUCTURED_PARAMETER)) { - scope.setStructuredParamDescription(reader.nextString()); + logger.debug("Found a structured scope, ignoring structure"); } else { logger.debug("found unexpected entry"); reader.skipValue(); From d92cc4d488eb2af0b9b6486dd3980ce64e4006d8 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 17 Jan 2017 17:06:18 -0500 Subject: [PATCH 112/269] fixed constants for spring 4.2 --- .../src/main/webapp/WEB-INF/views/approve.jsp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approve.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approve.jsp index 5624208c45..e9bdf36d1b 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approve.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approve.jsp @@ -1,7 +1,7 @@ <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ page import="org.springframework.security.core.AuthenticationException"%> <%@ page import="org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException"%> -<%@ page import="org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter"%> +<%@ page import="org.springframework.security.web.WebAttributes"%> <%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> @@ -13,12 +13,12 @@
- <% if (session.getAttribute(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY) != null && !(session.getAttribute(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY) instanceof UnapprovedClientAuthenticationException)) { %> + <% if (session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) != null && !(session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) instanceof UnapprovedClientAuthenticationException)) { %>
×

- (<%= ((AuthenticationException) session.getAttribute(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY)).getMessage() %>) + (<%= ((AuthenticationException) session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).getMessage() %>)

<% } %> From db50a88fe5806b8dd057a49a331e811079e2db7c Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 17 Jan 2017 17:09:14 -0500 Subject: [PATCH 113/269] Happy New Year 2017 --- checkstyle.xml | 2 +- openid-connect-client/pom.xml | 2 +- .../oauth2/introspectingfilter/IntrospectingTokenService.java | 2 +- .../mitre/oauth2/introspectingfilter/OAuth2AccessTokenImpl.java | 2 +- .../service/IntrospectionAuthorityGranter.java | 2 +- .../service/IntrospectionConfigurationService.java | 2 +- .../impl/JWTParsingIntrospectionConfigurationService.java | 2 +- .../service/impl/ScopeBasedIntrospectionAuthoritiesGranter.java | 2 +- .../service/impl/SimpleIntrospectionAuthorityGranter.java | 2 +- .../service/impl/StaticIntrospectionConfigurationService.java | 2 +- .../openid/connect/client/NamedAdminAuthoritiesMapper.java | 2 +- .../mitre/openid/connect/client/OIDCAuthenticationFilter.java | 2 +- .../mitre/openid/connect/client/OIDCAuthenticationProvider.java | 2 +- .../org/mitre/openid/connect/client/OIDCAuthoritiesMapper.java | 2 +- .../openid/connect/client/StaticPrefixTargetLinkURIChecker.java | 2 +- .../openid/connect/client/SubjectIssuerGrantedAuthority.java | 2 +- .../org/mitre/openid/connect/client/TargetLinkURIChecker.java | 2 +- .../java/org/mitre/openid/connect/client/UserInfoFetcher.java | 2 +- .../openid/connect/client/keypublisher/ClientKeyPublisher.java | 2 +- .../connect/client/keypublisher/ClientKeyPublisherMapping.java | 2 +- .../openid/connect/client/keypublisher/JwkViewResolver.java | 2 +- .../openid/connect/client/model/IssuerServiceResponse.java | 2 +- .../connect/client/service/AuthRequestOptionsService.java | 2 +- .../openid/connect/client/service/AuthRequestUrlBuilder.java | 2 +- .../connect/client/service/ClientConfigurationService.java | 2 +- .../org/mitre/openid/connect/client/service/IssuerService.java | 2 +- .../openid/connect/client/service/RegisteredClientService.java | 2 +- .../connect/client/service/ServerConfigurationService.java | 2 +- .../impl/DynamicRegistrationClientConfigurationService.java | 2 +- .../client/service/impl/DynamicServerConfigurationService.java | 2 +- .../client/service/impl/EncryptedAuthRequestUrlBuilder.java | 2 +- .../client/service/impl/HybridClientConfigurationService.java | 2 +- .../openid/connect/client/service/impl/HybridIssuerService.java | 2 +- .../client/service/impl/HybridServerConfigurationService.java | 2 +- .../client/service/impl/InMemoryRegisteredClientService.java | 2 +- .../client/service/impl/JsonFileRegisteredClientService.java | 2 +- .../connect/client/service/impl/PlainAuthRequestUrlBuilder.java | 2 +- .../client/service/impl/SignedAuthRequestUrlBuilder.java | 2 +- .../client/service/impl/StaticAuthRequestOptionsService.java | 2 +- .../client/service/impl/StaticClientConfigurationService.java | 2 +- .../client/service/impl/StaticServerConfigurationService.java | 2 +- .../connect/client/service/impl/StaticSingleIssuerService.java | 2 +- .../connect/client/service/impl/ThirdPartyIssuerService.java | 2 +- .../connect/client/service/impl/WebfingerIssuerService.java | 2 +- .../oauth2/introspectingfilter/TestOAuth2AccessTokenImpl.java | 2 +- .../impl/TestScopeBasedIntrospectionAuthoritiesGranter.java | 2 +- .../service/impl/TestHybridClientConfigurationService.java | 2 +- .../service/impl/TestHybridServerConfigurationService.java | 2 +- .../client/service/impl/TestPlainAuthRequestUrlBuilder.java | 2 +- .../client/service/impl/TestSignedAuthRequestUrlBuilder.java | 2 +- .../service/impl/TestStaticClientConfigurationService.java | 2 +- .../service/impl/TestStaticServerConfigurationService.java | 2 +- .../client/service/impl/TestThirdPartyIssuerService.java | 2 +- openid-connect-client/src/test/resources/test-context.xml | 2 +- openid-connect-common/pom.xml | 2 +- .../java/org/mitre/discovery/util/WebfingerURLNormalizer.java | 2 +- .../src/main/java/org/mitre/jose/keystore/JWKSetKeyStore.java | 2 +- .../main/java/org/mitre/jwt/assertion/AssertionValidator.java | 2 +- .../org/mitre/jwt/assertion/impl/NullAssertionValidator.java | 2 +- .../org/mitre/jwt/assertion/impl/SelfAssertionValidator.java | 2 +- .../jwt/assertion/impl/WhitelistedIssuerAssertionValidator.java | 2 +- .../encryption/service/JWTEncryptionAndDecryptionService.java | 2 +- .../service/impl/DefaultJWTEncryptionAndDecryptionService.java | 2 +- .../jwt/signer/service/JWTSigningAndValidationService.java | 2 +- .../mitre/jwt/signer/service/impl/ClientKeyCacheService.java | 2 +- .../service/impl/DefaultJWTSigningAndValidationService.java | 2 +- .../org/mitre/jwt/signer/service/impl/JWKSetCacheService.java | 2 +- .../service/impl/SymmetricKeyJWTValidatorCacheService.java | 2 +- .../java/org/mitre/oauth2/model/AuthenticationHolderEntity.java | 2 +- .../java/org/mitre/oauth2/model/AuthorizationCodeEntity.java | 2 +- .../main/java/org/mitre/oauth2/model/ClientDetailsEntity.java | 2 +- .../java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java | 2 +- .../java/org/mitre/oauth2/model/OAuth2RefreshTokenEntity.java | 2 +- .../src/main/java/org/mitre/oauth2/model/PKCEAlgorithm.java | 2 +- .../src/main/java/org/mitre/oauth2/model/RegisteredClient.java | 2 +- .../java/org/mitre/oauth2/model/RegisteredClientFields.java | 2 +- .../java/org/mitre/oauth2/model/SavedUserAuthentication.java | 2 +- .../src/main/java/org/mitre/oauth2/model/SystemScope.java | 2 +- .../mitre/oauth2/model/convert/JWEAlgorithmStringConverter.java | 2 +- .../model/convert/JWEEncryptionMethodStringConverter.java | 2 +- .../org/mitre/oauth2/model/convert/JWKSetStringConverter.java | 2 +- .../mitre/oauth2/model/convert/JWSAlgorithmStringConverter.java | 2 +- .../java/org/mitre/oauth2/model/convert/JWTStringConverter.java | 2 +- .../mitre/oauth2/model/convert/JsonElementStringConverter.java | 2 +- .../oauth2/model/convert/PKCEAlgorithmStringConverter.java | 2 +- .../mitre/oauth2/model/convert/SerializableStringConverter.java | 2 +- .../model/convert/SimpleGrantedAuthorityStringConverter.java | 2 +- .../mitre/oauth2/repository/AuthenticationHolderRepository.java | 2 +- .../mitre/oauth2/repository/AuthorizationCodeRepository.java | 2 +- .../org/mitre/oauth2/repository/OAuth2ClientRepository.java | 2 +- .../java/org/mitre/oauth2/repository/OAuth2TokenRepository.java | 2 +- .../java/org/mitre/oauth2/repository/SystemScopeRepository.java | 2 +- .../org/mitre/oauth2/service/ClientDetailsEntityService.java | 2 +- .../org/mitre/oauth2/service/IntrospectionResultAssembler.java | 2 +- .../java/org/mitre/oauth2/service/OAuth2TokenEntityService.java | 2 +- .../main/java/org/mitre/oauth2/service/SystemScopeService.java | 2 +- .../oauth2/service/impl/DefaultClientUserDetailsService.java | 2 +- .../oauth2/service/impl/UriEncodedClientUserDetailsService.java | 2 +- .../mitre/openid/connect/ClientDetailsEntityJsonProcessor.java | 2 +- .../openid/connect/config/ConfigurationBeanLocaleResolver.java | 2 +- .../openid/connect/config/ConfigurationPropertiesBean.java | 2 +- .../main/java/org/mitre/openid/connect/config/JWKSetEditor.java | 2 +- .../org/mitre/openid/connect/config/ServerConfiguration.java | 2 +- .../src/main/java/org/mitre/openid/connect/model/Address.java | 2 +- .../main/java/org/mitre/openid/connect/model/ApprovedSite.java | 2 +- .../java/org/mitre/openid/connect/model/BlacklistedSite.java | 2 +- .../main/java/org/mitre/openid/connect/model/CachedImage.java | 2 +- .../java/org/mitre/openid/connect/model/DefaultAddress.java | 2 +- .../java/org/mitre/openid/connect/model/DefaultUserInfo.java | 2 +- .../org/mitre/openid/connect/model/OIDCAuthenticationToken.java | 2 +- .../java/org/mitre/openid/connect/model/PairwiseIdentifier.java | 2 +- .../openid/connect/model/PendingOIDCAuthenticationToken.java | 2 +- .../src/main/java/org/mitre/openid/connect/model/UserInfo.java | 2 +- .../java/org/mitre/openid/connect/model/WhitelistedSite.java | 2 +- .../openid/connect/model/convert/JsonObjectStringConverter.java | 2 +- .../org/mitre/openid/connect/repository/AddressRepository.java | 2 +- .../mitre/openid/connect/repository/ApprovedSiteRepository.java | 2 +- .../openid/connect/repository/BlacklistedSiteRepository.java | 2 +- .../openid/connect/repository/PairwiseIdentifierRepository.java | 2 +- .../org/mitre/openid/connect/repository/UserInfoRepository.java | 2 +- .../openid/connect/repository/WhitelistedSiteRepository.java | 2 +- .../org/mitre/openid/connect/service/ApprovedSiteService.java | 2 +- .../mitre/openid/connect/service/BlacklistedSiteService.java | 2 +- .../mitre/openid/connect/service/ClientLogoLoadingService.java | 2 +- .../org/mitre/openid/connect/service/LoginHintExtracter.java | 2 +- .../org/mitre/openid/connect/service/MITREidDataService.java | 2 +- .../java/org/mitre/openid/connect/service/OIDCTokenService.java | 2 +- .../mitre/openid/connect/service/PairwiseIdentiferService.java | 2 +- .../openid/connect/service/ScopeClaimTranslationService.java | 2 +- .../java/org/mitre/openid/connect/service/StatsService.java | 2 +- .../java/org/mitre/openid/connect/service/UserInfoService.java | 2 +- .../mitre/openid/connect/service/WhitelistedSiteService.java | 2 +- .../src/main/java/org/mitre/openid/connect/view/JWKSetView.java | 2 +- .../java/org/mitre/openid/connect/web/UserInfoInterceptor.java | 2 +- .../src/main/java/org/mitre/uma/model/Claim.java | 2 +- .../main/java/org/mitre/uma/model/ClaimProcessingResult.java | 2 +- .../src/main/java/org/mitre/uma/model/Permission.java | 2 +- .../src/main/java/org/mitre/uma/model/PermissionTicket.java | 2 +- .../src/main/java/org/mitre/uma/model/Policy.java | 2 +- .../src/main/java/org/mitre/uma/model/ResourceSet.java | 2 +- .../main/java/org/mitre/uma/model/SavedRegisteredClient.java | 2 +- .../uma/model/convert/RegisteredClientStringConverter.java | 2 +- .../java/org/mitre/uma/repository/PermissionRepository.java | 2 +- .../java/org/mitre/uma/repository/ResourceSetRepository.java | 2 +- .../java/org/mitre/uma/service/ClaimsProcessingService.java | 2 +- .../src/main/java/org/mitre/uma/service/PermissionService.java | 2 +- .../src/main/java/org/mitre/uma/service/ResourceSetService.java | 2 +- .../src/main/java/org/mitre/uma/service/UmaTokenService.java | 2 +- .../src/main/java/org/mitre/util/JsonUtils.java | 2 +- .../src/main/java/org/mitre/util/jpa/JpaUtil.java | 2 +- .../org/mitre/discovery/util/TestWebfingerURLNormalizer.java | 2 +- .../src/test/java/org/mitre/jose/TestJWKSetKeyStore.java | 2 +- .../impl/TestDefaultJWTEncryptionAndDecryptionService.java | 2 +- .../java/org/mitre/oauth2/model/ClientDetailsEntityTest.java | 2 +- .../test/java/org/mitre/oauth2/model/RegisteredClientTest.java | 2 +- .../openid/connect/ClientDetailsEntityJsonProcessorTest.java | 2 +- .../openid/connect/config/ConfigurationPropertiesBeanTest.java | 2 +- .../mitre/openid/connect/config/ServerConfigurationTest.java | 2 +- openid-connect-server-webapp/pom.xml | 2 +- openid-connect-server-webapp/src/main/resources/log4j.xml | 2 +- .../src/main/webapp/WEB-INF/application-context.xml | 2 +- .../src/main/webapp/WEB-INF/assertion-config.xml | 2 +- .../src/main/webapp/WEB-INF/authz-config.xml | 2 +- .../src/main/webapp/WEB-INF/crypto-config.xml | 2 +- .../src/main/webapp/WEB-INF/data-context.xml | 2 +- .../src/main/webapp/WEB-INF/jpa-config.xml | 2 +- .../src/main/webapp/WEB-INF/local-config.xml | 2 +- .../src/main/webapp/WEB-INF/server-config.xml | 2 +- .../src/main/webapp/WEB-INF/spring-servlet.xml | 2 +- .../src/main/webapp/WEB-INF/task-config.xml | 2 +- .../src/main/webapp/WEB-INF/user-context.xml | 2 +- openid-connect-server-webapp/src/main/webapp/WEB-INF/web.xml | 2 +- .../src/main/webapp/resources/js/admin.js | 2 +- .../src/main/webapp/resources/js/blacklist.js | 2 +- .../src/main/webapp/resources/js/client.js | 2 +- .../src/main/webapp/resources/js/dynreg.js | 2 +- .../src/main/webapp/resources/js/grant.js | 2 +- .../src/main/webapp/resources/js/rsreg.js | 2 +- .../src/main/webapp/resources/js/scope.js | 2 +- .../src/main/webapp/resources/js/token.js | 2 +- .../src/main/webapp/resources/js/whitelist.js | 2 +- .../src/main/webapp/resources/template/admin.html | 2 +- .../src/main/webapp/resources/template/blacklist.html | 2 +- .../src/main/webapp/resources/template/client.html | 2 +- .../src/main/webapp/resources/template/dynreg.html | 2 +- .../src/main/webapp/resources/template/grant.html | 2 +- .../src/main/webapp/resources/template/rsreg.html | 2 +- .../src/main/webapp/resources/template/scope.html | 2 +- .../src/main/webapp/resources/template/token.html | 2 +- .../src/main/webapp/resources/template/whitelist.html | 2 +- openid-connect-server/pom.xml | 2 +- .../src/main/java/org/mitre/discovery/view/WebfingerView.java | 2 +- .../main/java/org/mitre/discovery/web/DiscoveryEndpoint.java | 2 +- .../mitre/oauth2/assertion/AssertionOAuth2RequestFactory.java | 2 +- .../mitre/oauth2/assertion/impl/DirectCopyRequestFactory.java | 2 +- .../org/mitre/oauth2/exception/DuplicateClientIdException.java | 2 +- .../repository/impl/JpaAuthenticationHolderRepository.java | 2 +- .../oauth2/repository/impl/JpaAuthorizationCodeRepository.java | 2 +- .../mitre/oauth2/repository/impl/JpaOAuth2ClientRepository.java | 2 +- .../mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java | 2 +- .../mitre/oauth2/repository/impl/JpaSystemScopeRepository.java | 2 +- .../oauth2/service/impl/BlacklistAwareRedirectResolver.java | 2 +- .../service/impl/DefaultIntrospectionResultAssembler.java | 2 +- .../service/impl/DefaultOAuth2AuthorizationCodeService.java | 2 +- .../service/impl/DefaultOAuth2ClientDetailsEntityService.java | 2 +- .../oauth2/service/impl/DefaultOAuth2ProviderTokenService.java | 2 +- .../mitre/oauth2/service/impl/DefaultSystemScopeService.java | 2 +- .../main/java/org/mitre/oauth2/token/ChainedTokenGranter.java | 2 +- .../java/org/mitre/oauth2/token/JWTAssertionTokenGranter.java | 2 +- .../oauth2/token/ScopeServiceAwareOAuth2RequestValidator.java | 2 +- .../src/main/java/org/mitre/oauth2/view/TokenApiView.java | 2 +- .../main/java/org/mitre/oauth2/web/AuthenticationUtilities.java | 2 +- .../src/main/java/org/mitre/oauth2/web/CorsFilter.java | 2 +- .../main/java/org/mitre/oauth2/web/IntrospectionEndpoint.java | 2 +- .../main/java/org/mitre/oauth2/web/OAuth2ExceptionHandler.java | 2 +- .../java/org/mitre/oauth2/web/OAuthConfirmationController.java | 2 +- .../src/main/java/org/mitre/oauth2/web/RevocationEndpoint.java | 2 +- .../src/main/java/org/mitre/oauth2/web/ScopeAPI.java | 2 +- .../src/main/java/org/mitre/oauth2/web/TokenAPI.java | 2 +- .../assertion/JWTBearerAssertionAuthenticationToken.java | 2 +- .../connect/assertion/JWTBearerAuthenticationProvider.java | 2 +- .../assertion/JWTBearerClientAssertionTokenEndpointFilter.java | 2 +- .../java/org/mitre/openid/connect/config/JsonMessageSource.java | 2 +- .../org/mitre/openid/connect/exception/ValidationException.java | 2 +- .../mitre/openid/connect/filter/AuthorizationRequestFilter.java | 2 +- .../org/mitre/openid/connect/filter/MultiUrlRequestMatcher.java | 2 +- .../openid/connect/repository/impl/JpaAddressRepository.java | 2 +- .../connect/repository/impl/JpaApprovedSiteRepository.java | 2 +- .../connect/repository/impl/JpaBlacklistedSiteRepository.java | 2 +- .../repository/impl/JpaPairwiseIdentifierRepository.java | 2 +- .../openid/connect/repository/impl/JpaUserInfoRepository.java | 2 +- .../connect/repository/impl/JpaWhitelistedSiteRepository.java | 2 +- .../openid/connect/request/ConnectOAuth2RequestFactory.java | 2 +- .../mitre/openid/connect/request/ConnectRequestParameters.java | 2 +- .../openid/connect/service/impl/DefaultApprovedSiteService.java | 2 +- .../connect/service/impl/DefaultBlacklistedSiteService.java | 2 +- .../openid/connect/service/impl/DefaultOIDCTokenService.java | 2 +- .../service/impl/DefaultScopeClaimTranslationService.java | 2 +- .../mitre/openid/connect/service/impl/DefaultStatsService.java | 2 +- .../openid/connect/service/impl/DefaultUserInfoService.java | 2 +- .../connect/service/impl/DefaultWhitelistedSiteService.java | 2 +- .../openid/connect/service/impl/DummyResourceSetService.java | 2 +- .../connect/service/impl/InMemoryClientLogoLoadingService.java | 2 +- .../openid/connect/service/impl/MITREidDataServiceSupport.java | 2 +- .../openid/connect/service/impl/MITREidDataService_1_0.java | 2 +- .../openid/connect/service/impl/MITREidDataService_1_1.java | 2 +- .../openid/connect/service/impl/MITREidDataService_1_2.java | 2 +- .../openid/connect/service/impl/MITREidDataService_1_3.java | 2 +- .../connect/service/impl/MatchLoginHintsAgainstUsers.java | 2 +- .../mitre/openid/connect/service/impl/PassAllLoginHints.java | 2 +- .../openid/connect/service/impl/RemoveLoginHintsWithHTTP.java | 2 +- .../connect/service/impl/UUIDPairwiseIdentiferService.java | 2 +- .../org/mitre/openid/connect/token/ConnectTokenEnhancer.java | 2 +- .../org/mitre/openid/connect/token/TofuUserApprovalHandler.java | 2 +- .../java/org/mitre/openid/connect/util/IdTokenHashUtils.java | 2 +- .../org/mitre/openid/connect/view/AbstractClientEntityView.java | 2 +- .../mitre/openid/connect/view/ClientEntityViewForAdmins.java | 2 +- .../org/mitre/openid/connect/view/ClientEntityViewForUsers.java | 2 +- .../openid/connect/view/ClientInformationResponseView.java | 2 +- .../main/java/org/mitre/openid/connect/view/HttpCodeView.java | 2 +- .../org/mitre/openid/connect/view/JsonApprovedSiteView.java | 2 +- .../main/java/org/mitre/openid/connect/view/JsonEntityView.java | 2 +- .../main/java/org/mitre/openid/connect/view/JsonErrorView.java | 2 +- .../java/org/mitre/openid/connect/view/UserInfoJWTView.java | 2 +- .../main/java/org/mitre/openid/connect/view/UserInfoView.java | 2 +- .../main/java/org/mitre/openid/connect/web/ApprovedSiteAPI.java | 2 +- .../org/mitre/openid/connect/web/AuthenticationTimeStamper.java | 2 +- .../main/java/org/mitre/openid/connect/web/BlacklistAPI.java | 2 +- .../src/main/java/org/mitre/openid/connect/web/ClientAPI.java | 2 +- .../src/main/java/org/mitre/openid/connect/web/DataAPI.java | 2 +- .../openid/connect/web/DynamicClientRegistrationEndpoint.java | 2 +- .../org/mitre/openid/connect/web/JWKSetPublishingEndpoint.java | 2 +- .../connect/web/ProtectedResourceRegistrationEndpoint.java | 2 +- .../main/java/org/mitre/openid/connect/web/RootController.java | 2 +- .../org/mitre/openid/connect/web/ServerConfigInterceptor.java | 2 +- .../src/main/java/org/mitre/openid/connect/web/StatsAPI.java | 2 +- .../java/org/mitre/openid/connect/web/UserInfoEndpoint.java | 2 +- .../main/java/org/mitre/openid/connect/web/WhitelistAPI.java | 2 +- .../oauth2/service/impl/TestBlacklistAwareRedirectResolver.java | 2 +- .../service/impl/TestDefaultIntrospectionResultAssembler.java | 2 +- .../impl/TestDefaultOAuth2ClientDetailsEntityService.java | 2 +- .../service/impl/TestDefaultOAuth2ProviderTokenService.java | 2 +- .../oauth2/service/impl/TestDefaultSystemScopeService.java | 2 +- .../connect/service/impl/TestDefaultApprovedSiteService.java | 2 +- .../connect/service/impl/TestDefaultBlacklistedSiteService.java | 2 +- .../openid/connect/service/impl/TestDefaultStatsService.java | 2 +- .../openid/connect/service/impl/TestDefaultUserInfoService.java | 2 +- .../connect/service/impl/TestDefaultWhitelistedSiteService.java | 2 +- .../openid/connect/service/impl/TestMITREidDataService_1_0.java | 2 +- .../openid/connect/service/impl/TestMITREidDataService_1_1.java | 2 +- .../openid/connect/service/impl/TestMITREidDataService_1_2.java | 2 +- .../openid/connect/service/impl/TestMITREidDataService_1_3.java | 2 +- .../connect/service/impl/TestUUIDPairwiseIdentiferService.java | 2 +- .../org/mitre/openid/connect/util/TestIdTokenHashUtils.java | 2 +- pom.xml | 2 +- uma-server-webapp/pom.xml | 2 +- .../openid/connect/service/impl/MITREidDataService_1_2.java | 2 +- .../src/main/webapp/WEB-INF/application-context.xml | 2 +- uma-server-webapp/src/main/webapp/WEB-INF/server-config.xml | 2 +- uma-server-webapp/src/main/webapp/WEB-INF/user-context.xml | 2 +- uma-server-webapp/src/main/webapp/resources/js/admin.js | 2 +- uma-server-webapp/src/main/webapp/resources/js/policy.js | 2 +- .../src/main/webapp/resources/template/policy.html | 2 +- uma-server/pom.xml | 2 +- .../org/mitre/uma/repository/impl/JpaPermissionRepository.java | 2 +- .../org/mitre/uma/repository/impl/JpaResourceSetRepository.java | 2 +- .../org/mitre/uma/service/impl/DefaultPermissionService.java | 2 +- .../org/mitre/uma/service/impl/DefaultResourceSetService.java | 2 +- .../java/org/mitre/uma/service/impl/DefaultUmaTokenService.java | 2 +- .../org/mitre/uma/service/impl/JpaRegisteredClientService.java | 2 +- .../org/mitre/uma/service/impl/MatchAllClaimsOnAnyPolicy.java | 2 +- .../java/org/mitre/uma/util/ExternalLoginAuthoritiesMapper.java | 2 +- .../org/mitre/uma/view/ResourceSetEntityAbbreviatedView.java | 2 +- .../src/main/java/org/mitre/uma/view/ResourceSetEntityView.java | 2 +- .../java/org/mitre/uma/web/AuthorizationRequestEndpoint.java | 2 +- .../main/java/org/mitre/uma/web/ClaimsCollectionEndpoint.java | 2 +- .../java/org/mitre/uma/web/PermissionRegistrationEndpoint.java | 2 +- uma-server/src/main/java/org/mitre/uma/web/PolicyAPI.java | 2 +- .../java/org/mitre/uma/web/ResourceSetRegistrationEndpoint.java | 2 +- .../src/main/java/org/mitre/uma/web/UmaDiscoveryEndpoint.java | 2 +- .../src/main/java/org/mitre/uma/web/UserClaimSearchHelper.java | 2 +- .../mitre/uma/service/impl/TestDefaultPermissionService.java | 2 +- .../mitre/uma/service/impl/TestDefaultResourceSetService.java | 2 +- 323 files changed, 323 insertions(+), 323 deletions(-) diff --git a/checkstyle.xml b/checkstyle.xml index f95d9e3b0e..b23f55acf4 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -1,6 +1,6 @@ - + - + @@ -118,7 +118,7 @@ - + @@ -126,14 +126,14 @@ - + - + @@ -144,7 +144,7 @@ create-session="stateless" authentication-manager-ref="clientAuthenticationManager"> - + @@ -157,7 +157,7 @@ create-session="stateless" authentication-manager-ref="clientAuthenticationManager"> - + From 777b7a2112a332b18bcfa4a965eec54d4fd6b45c Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 18 Jan 2017 16:47:15 -0500 Subject: [PATCH 116/269] added new data package to logging defaults --- openid-connect-server-webapp/src/main/resources/log4j.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openid-connect-server-webapp/src/main/resources/log4j.xml b/openid-connect-server-webapp/src/main/resources/log4j.xml index 72e8319561..38d1be8925 100644 --- a/openid-connect-server-webapp/src/main/resources/log4j.xml +++ b/openid-connect-server-webapp/src/main/resources/log4j.xml @@ -48,6 +48,9 @@ + + + From 52d2298f99efe27df925415f400212233a92ea75 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 18 Jan 2017 16:47:38 -0500 Subject: [PATCH 117/269] begin modularization of data import/export API --- .../service/RegisteredClientService.java | 2 + .../connect/service/MITREidDataService.java | 9 + .../service/MITREidDataServiceExtension.java | 68 ++ .../service/SavedRegisteredClientService.java | 45 ++ .../service/impl/MITREidDataService_1_0.java | 26 + .../service/impl/MITREidDataService_1_1.java | 26 + .../service/impl/MITREidDataService_1_2.java | 26 + .../service/impl/MITREidDataService_1_3.java | 42 +- .../org/mitre/openid/connect/web/DataAPI.java | 38 +- .../impl/JpaRegisteredClientService.java | 4 +- .../impl/UmaDataServiceExtension_1_3.java | 643 ++++++++++++++++++ 11 files changed, 907 insertions(+), 22 deletions(-) create mode 100644 openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceExtension.java create mode 100644 openid-connect-common/src/main/java/org/mitre/uma/service/SavedRegisteredClientService.java create mode 100644 uma-server/src/main/java/org/mitre/uma/service/impl/UmaDataServiceExtension_1_3.java diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/RegisteredClientService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/RegisteredClientService.java index 8834e015bd..ed4bd2be70 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/RegisteredClientService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/RegisteredClientService.java @@ -19,6 +19,8 @@ */ package org.mitre.openid.connect.client.service; +import java.util.List; + import org.mitre.oauth2.model.RegisteredClient; /** diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataService.java index 448f4af9d8..ec07f00359 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataService.java @@ -60,4 +60,13 @@ public interface MITREidDataService { */ void importData(JsonReader reader) throws IOException; + /** + * Return true if the this data service supports the given version. This is called before + * handing the service the reader through its importData function. + * + * @param version + * @return + */ + boolean supportsVersion(String version); + } \ No newline at end of file diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceExtension.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceExtension.java new file mode 100644 index 0000000000..eff90288e8 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceExtension.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.openid.connect.service; + +import java.io.IOException; + +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +/** + * A modular extension to the data import/export layer. Any instances of this need to be + * declared as beans to be picked up by the data services. + * + * @author jricher + * + */ +public interface MITREidDataServiceExtension { + + /** + * Export any data for this extension. This is called from the top level object. + * All extensions MUST return the writer to a state such that another member of + * the top level object can be written next. + * + * @param writer + */ + void exportExtensionData(JsonWriter writer) throws IOException; + + /** + * Import data that's part of this extension. This is called from the context of + * reading the top level object. All extensions MUST return the reader to a state + * such that another member of the top level object can be read next. The name of + * the data element being imported is passed in as name. If the extension does not + * support this data element, it must return without advancing the reader. + * + * Returns "true" if the item was processed, "false" otherwise. + * + * @param reader + */ + boolean importExtensionData(String name, JsonReader reader) throws IOException; + + /** + * Signal the extension to wrap up all object processing and finalize its + */ + void fixExtensionObjectReferences(); + + /** + * Return + * @param mitreidConnect13 + * @return + */ + boolean supportsVersion(String version); + +} diff --git a/openid-connect-common/src/main/java/org/mitre/uma/service/SavedRegisteredClientService.java b/openid-connect-common/src/main/java/org/mitre/uma/service/SavedRegisteredClientService.java new file mode 100644 index 0000000000..ee3e5329a7 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/uma/service/SavedRegisteredClientService.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.uma.service; + +import java.util.Collection; + +import org.mitre.oauth2.model.RegisteredClient; +import org.mitre.uma.model.SavedRegisteredClient; + +/** + * @author jricher + * + */ +public interface SavedRegisteredClientService { + + /** + * Get a list of all the registered clients that we know about. + * + * @return + */ + Collection getAll(); + + /** + * @param issuer + * @param client + */ + void save(String issuer, RegisteredClient client); + + +} diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java index d1040e6c9b..0560032d74 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java @@ -19,10 +19,12 @@ import java.io.IOException; import java.text.ParseException; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -46,6 +48,7 @@ import org.mitre.openid.connect.repository.BlacklistedSiteRepository; import org.mitre.openid.connect.repository.WhitelistedSiteRepository; import org.mitre.openid.connect.service.MITREidDataService; +import org.mitre.openid.connect.service.MITREidDataServiceExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -96,6 +99,15 @@ public class MITREidDataService_1_0 extends MITREidDataServiceSupport implements private OAuth2TokenRepository tokenRepository; @Autowired private SystemScopeRepository sysScopeRepository; + @Autowired(required = false) + private List extensions = Collections.emptyList(); + + private static final String THIS_VERSION = MITREID_CONNECT_1_0; + + @Override + public boolean supportsVersion(String version) { + return THIS_VERSION.equals(version); + } /* (non-Javadoc) * @see org.mitre.openid.connect.service.MITREidDataService#export(com.google.gson.stream.JsonWriter) @@ -140,6 +152,14 @@ public void importData(JsonReader reader) throws IOException { } else if (name.equals(SYSTEMSCOPES)) { readSystemScopes(reader); } else { + for (MITREidDataServiceExtension extension : extensions) { + if (extension.supportsVersion(THIS_VERSION)) { + if (extension.canImport(name)) { + extension.importExtensionData(reader); + break; + } + } + } // unknown token, skip it reader.skipValue(); } @@ -154,6 +174,12 @@ public void importData(JsonReader reader) throws IOException { continue; } } fixObjectReferences(); + for (MITREidDataServiceExtension extension : extensions) { + if (extension.supportsVersion(THIS_VERSION)) { + extension.fixExtensionObjectReferences(); + break; + } + } } private Map refreshTokenToClientRefs = new HashMap<>(); private Map refreshTokenToAuthHolderRefs = new HashMap<>(); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java index 7fbeb335db..ca976aac4a 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java @@ -20,10 +20,12 @@ import java.io.Serializable; import java.text.ParseException; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -48,6 +50,7 @@ import org.mitre.openid.connect.repository.BlacklistedSiteRepository; import org.mitre.openid.connect.repository.WhitelistedSiteRepository; import org.mitre.openid.connect.service.MITREidDataService; +import org.mitre.openid.connect.service.MITREidDataServiceExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -99,6 +102,15 @@ public class MITREidDataService_1_1 extends MITREidDataServiceSupport implements private OAuth2TokenRepository tokenRepository; @Autowired private SystemScopeRepository sysScopeRepository; + @Autowired(required = false) + private List extensions = Collections.emptyList(); + + private static final String THIS_VERSION = MITREID_CONNECT_1_1; + + @Override + public boolean supportsVersion(String version) { + return THIS_VERSION.equals(version); + } /* (non-Javadoc) * @see org.mitre.openid.connect.service.MITREidDataService#export(com.google.gson.stream.JsonWriter) @@ -142,6 +154,14 @@ public void importData(JsonReader reader) throws IOException { } else if (name.equals(SYSTEMSCOPES)) { readSystemScopes(reader); } else { + for (MITREidDataServiceExtension extension : extensions) { + if (extension.supportsVersion(THIS_VERSION)) { + if (extension.canImport(name)) { + extension.importExtensionData(reader); + break; + } + } + } // unknown token, skip it reader.skipValue(); } @@ -157,6 +177,12 @@ public void importData(JsonReader reader) throws IOException { } } fixObjectReferences(); + for (MITREidDataServiceExtension extension : extensions) { + if (extension.supportsVersion(THIS_VERSION)) { + extension.fixExtensionObjectReferences(); + break; + } + } } private Map refreshTokenToClientRefs = new HashMap<>(); private Map refreshTokenToAuthHolderRefs = new HashMap<>(); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java index 00d6fb7837..85d3141728 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java @@ -19,9 +19,11 @@ import java.io.IOException; import java.io.Serializable; import java.text.ParseException; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -46,6 +48,7 @@ import org.mitre.openid.connect.repository.BlacklistedSiteRepository; import org.mitre.openid.connect.repository.WhitelistedSiteRepository; import org.mitre.openid.connect.service.MITREidDataService; +import org.mitre.openid.connect.service.MITREidDataServiceExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -162,6 +165,15 @@ public class MITREidDataService_1_2 extends MITREidDataServiceSupport implements private OAuth2TokenRepository tokenRepository; @Autowired private SystemScopeRepository sysScopeRepository; + @Autowired(required = false) + private List extensions = Collections.emptyList(); + + private static final String THIS_VERSION = MITREID_CONNECT_1_2; + + @Override + public boolean supportsVersion(String version) { + return THIS_VERSION.equals(version); + } /* (non-Javadoc) * @see org.mitre.openid.connect.service.MITREidDataService#export(com.google.gson.stream.JsonWriter) @@ -206,6 +218,14 @@ public void importData(JsonReader reader) throws IOException { } else if (name.equals(SYSTEMSCOPES)) { readSystemScopes(reader); } else { + for (MITREidDataServiceExtension extension : extensions) { + if (extension.supportsVersion(THIS_VERSION)) { + if (extension.canImport(name)) { + extension.importExtensionData(reader); + break; + } + } + } // unknown token, skip it reader.skipValue(); } @@ -221,6 +241,12 @@ public void importData(JsonReader reader) throws IOException { } } fixObjectReferences(); + for (MITREidDataServiceExtension extension : extensions) { + if (extension.supportsVersion(THIS_VERSION)) { + extension.fixExtensionObjectReferences(); + break; + } + } } private Map refreshTokenToClientRefs = new HashMap(); private Map refreshTokenToAuthHolderRefs = new HashMap(); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java index 90f3cf7f41..ca1a7995fd 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.Serializable; import java.text.ParseException; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -52,6 +53,7 @@ import org.mitre.openid.connect.repository.BlacklistedSiteRepository; import org.mitre.openid.connect.repository.WhitelistedSiteRepository; import org.mitre.openid.connect.service.MITREidDataService; +import org.mitre.openid.connect.service.MITREidDataServiceExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -165,6 +167,15 @@ public class MITREidDataService_1_3 extends MITREidDataServiceSupport implements private OAuth2TokenRepository tokenRepository; @Autowired private SystemScopeRepository sysScopeRepository; + @Autowired(required = false) + private List extensions = Collections.emptyList(); + + private static final String THIS_VERSION = MITREID_CONNECT_1_3; + + @Override + public boolean supportsVersion(String version) { + return THIS_VERSION.equals(version); + } /* (non-Javadoc) * @see org.mitre.openid.connect.service.MITREidDataService#export(com.google.gson.stream.JsonWriter) @@ -173,7 +184,7 @@ public class MITREidDataService_1_3 extends MITREidDataServiceSupport implements public void exportData(JsonWriter writer) throws IOException { // version tag at the root - writer.name(MITREID_CONNECT_1_3); + writer.name(THIS_VERSION); writer.beginObject(); @@ -217,6 +228,13 @@ public void exportData(JsonWriter writer) throws IOException { writer.beginArray(); writeSystemScopes(writer); writer.endArray(); + + for (MITREidDataServiceExtension extension : extensions) { + if (extension.supportsVersion(THIS_VERSION)) { + extension.exportExtensionData(writer); + break; + } + } writer.endObject(); // end mitreid-connect-1.2 } @@ -567,6 +585,15 @@ public void importData(JsonReader reader) throws IOException { } else if (name.equals(SYSTEMSCOPES)) { readSystemScopes(reader); } else { + for (MITREidDataServiceExtension extension : extensions) { + if (extension.supportsVersion(THIS_VERSION)) { + if (extension.importExtensionData(name, reader)) { + // if the extension processed data, break out of this inner loop + // (only the first extension to claim an extension point gets it) + break; + } + } + } // unknown token, skip it reader.skipValue(); } @@ -582,6 +609,12 @@ public void importData(JsonReader reader) throws IOException { } } fixObjectReferences(); + for (MITREidDataServiceExtension extension : extensions) { + if (extension.supportsVersion(THIS_VERSION)) { + extension.fixExtensionObjectReferences(); + break; + } + } } private Map refreshTokenToClientRefs = new HashMap(); private Map refreshTokenToAuthHolderRefs = new HashMap(); @@ -841,8 +874,8 @@ private SavedUserAuthentication readSavedUserAuthentication(JsonReader reader) t return savedUserAuth; } - Map grantOldToNewIdMap = new HashMap<>(); - Map> grantToAccessTokensRefs = new HashMap<>(); + private Map grantOldToNewIdMap = new HashMap<>(); + private Map> grantToAccessTokensRefs = new HashMap<>(); /** * @param reader @@ -905,7 +938,8 @@ private void readGrants(JsonReader reader) throws IOException { reader.endArray(); logger.info("Done reading grants"); } - Map whitelistedSiteOldToNewIdMap = new HashMap(); + + private Map whitelistedSiteOldToNewIdMap = new HashMap(); /** * @param reader diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java index be546876b5..39b4584b01 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java @@ -21,6 +21,7 @@ import java.security.Principal; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.List; import javax.servlet.http.HttpServletResponse; @@ -40,6 +41,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; +import com.google.common.collect.ImmutableList; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; @@ -69,16 +71,16 @@ public class DataAPI { private ConfigurationPropertiesBean config; @Autowired - private MITREidDataService_1_0 dataService_1_0; - - @Autowired - private MITREidDataService_1_1 dataService_1_1; - - @Autowired - private MITREidDataService_1_2 dataService_1_2; + private List importers; + + private List supportedVersions = ImmutableList.of( + MITREidDataService.MITREID_CONNECT_1_0, + MITREidDataService.MITREID_CONNECT_1_1, + MITREidDataService.MITREID_CONNECT_1_2, + MITREidDataService.MITREID_CONNECT_1_3); @Autowired - private MITREidDataService_1_3 dataService_1_3; + private MITREidDataService_1_3 exporter; @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) public String importData(Reader in, Model m) throws IOException { @@ -92,14 +94,16 @@ public String importData(Reader in, Model m) throws IOException { switch (tok) { case NAME: String name = reader.nextName(); - if (name.equals(MITREidDataService.MITREID_CONNECT_1_0)) { - dataService_1_0.importData(reader); - } else if (name.equals(MITREidDataService.MITREID_CONNECT_1_1)) { - dataService_1_1.importData(reader); - } else if (name.equals(MITREidDataService.MITREID_CONNECT_1_2)) { - dataService_1_2.importData(reader); - } else if (name.equals(MITREidDataService.MITREID_CONNECT_1_3)) { - dataService_1_3.importData(reader); + + if (supportedVersions.contains(name)) { + // we're working with a known data version tag + for (MITREidDataService dataService : importers) { + // dispatch to the correct service + if (dataService.supportsVersion(name)) { + dataService.importData(reader); + break; + } + } } else { // consume the next bit silently for now logger.debug("Skipping value for " + name); // TODO: write these out? @@ -140,7 +144,7 @@ public void exportData(HttpServletResponse resp, Principal prin) throws IOExcept writer.value(prin.getName()); // delegate to the service to do the actual export - dataService_1_3.exportData(writer); + exporter.exportData(writer); writer.endObject(); // end root writer.close(); diff --git a/uma-server/src/main/java/org/mitre/uma/service/impl/JpaRegisteredClientService.java b/uma-server/src/main/java/org/mitre/uma/service/impl/JpaRegisteredClientService.java index 3638f4a419..85ea7f5a14 100644 --- a/uma-server/src/main/java/org/mitre/uma/service/impl/JpaRegisteredClientService.java +++ b/uma-server/src/main/java/org/mitre/uma/service/impl/JpaRegisteredClientService.java @@ -26,6 +26,7 @@ import org.mitre.oauth2.model.RegisteredClient; import org.mitre.openid.connect.client.service.RegisteredClientService; import org.mitre.uma.model.SavedRegisteredClient; +import org.mitre.uma.service.SavedRegisteredClientService; import org.mitre.util.jpa.JpaUtil; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -35,7 +36,7 @@ * */ @Service -public class JpaRegisteredClientService implements RegisteredClientService { +public class JpaRegisteredClientService implements RegisteredClientService, SavedRegisteredClientService{ @PersistenceContext(unitName="defaultPersistenceUnit") private EntityManager em; @@ -86,6 +87,7 @@ private SavedRegisteredClient getSavedRegisteredClientFromStorage(String issuer) /** * @return */ + @Override public Collection getAll() { TypedQuery query = em.createQuery("SELECT c from SavedRegisteredClient c", SavedRegisteredClient.class); return query.getResultList(); diff --git a/uma-server/src/main/java/org/mitre/uma/service/impl/UmaDataServiceExtension_1_3.java b/uma-server/src/main/java/org/mitre/uma/service/impl/UmaDataServiceExtension_1_3.java new file mode 100644 index 0000000000..41080c4e45 --- /dev/null +++ b/uma-server/src/main/java/org/mitre/uma/service/impl/UmaDataServiceExtension_1_3.java @@ -0,0 +1,643 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.uma.service.impl; + +import static org.mitre.util.JsonUtils.*; + +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.mitre.oauth2.model.RegisteredClient; +import org.mitre.openid.connect.ClientDetailsEntityJsonProcessor; +import org.mitre.openid.connect.service.MITREidDataService; +import org.mitre.openid.connect.service.MITREidDataServiceExtension; +import org.mitre.openid.connect.service.impl.MITREidDataServiceSupport; +import org.mitre.uma.model.Claim; +import org.mitre.uma.model.Permission; +import org.mitre.uma.model.PermissionTicket; +import org.mitre.uma.model.Policy; +import org.mitre.uma.model.ResourceSet; +import org.mitre.uma.model.SavedRegisteredClient; +import org.mitre.uma.repository.PermissionRepository; +import org.mitre.uma.repository.ResourceSetRepository; +import org.mitre.uma.service.SavedRegisteredClientService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +/** + * @author jricher + * + */ +public class UmaDataServiceExtension_1_3 extends MITREidDataServiceSupport implements MITREidDataServiceExtension { + + private static final String THIS_VERSION = MITREidDataService.MITREID_CONNECT_1_3; + + private static final String REGISTERED_CLIENT = "registeredClient"; + private static final String DEFAULT_SCOPE = "defaultScope"; + private static final String STRUCTURED_PARAMETER = "structuredParameter"; + private static final String STRUCTURED = "structured"; + private static final String RESTRICTED = "restricted"; + private static final String ICON = "icon"; + private static final String DYNAMICALLY_REGISTERED = "dynamicallyRegistered"; + private static final String CLEAR_ACCESS_TOKENS_ON_REFRESH = "clearAccessTokensOnRefresh"; + private static final String REUSE_REFRESH_TOKEN = "reuseRefreshToken"; + private static final String ALLOW_INTROSPECTION = "allowIntrospection"; + private static final String DESCRIPTION = "description"; + private static final String REQUEST_URIS = "requestUris"; + private static final String POST_LOGOUT_REDIRECT_URI = "postLogoutRedirectUri"; + private static final String INTITATE_LOGIN_URI = "intitateLoginUri"; + private static final String DEFAULT_ACR_VALUES = "defaultACRValues"; + private static final String REQUIRE_AUTH_TIME = "requireAuthTime"; + private static final String DEFAULT_MAX_AGE = "defaultMaxAge"; + private static final String TOKEN_ENDPOINT_AUTH_SIGNING_ALG = "tokenEndpointAuthSigningAlg"; + private static final String USER_INFO_ENCRYPTED_RESPONSE_ENC = "userInfoEncryptedResponseEnc"; + private static final String USER_INFO_ENCRYPTED_RESPONSE_ALG = "userInfoEncryptedResponseAlg"; + private static final String USER_INFO_SIGNED_RESPONSE_ALG = "userInfoSignedResponseAlg"; + private static final String ID_TOKEN_ENCRYPTED_RESPONSE_ENC = "idTokenEncryptedResponseEnc"; + private static final String ID_TOKEN_ENCRYPTED_RESPONSE_ALG = "idTokenEncryptedResponseAlg"; + private static final String ID_TOKEN_SIGNED_RESPONSE_ALG = "idTokenSignedResponseAlg"; + private static final String REQUEST_OBJECT_SIGNING_ALG = "requestObjectSigningAlg"; + private static final String SUBJECT_TYPE = "subjectType"; + private static final String SECTOR_IDENTIFIER_URI = "sectorIdentifierUri"; + private static final String APPLICATION_TYPE = "applicationType"; + private static final String JWKS = "jwks"; + private static final String JWKS_URI = "jwksUri"; + private static final String POLICY_URI = "policyUri"; + private static final String GRANT_TYPES = "grantTypes"; + private static final String TOKEN_ENDPOINT_AUTH_METHOD = "tokenEndpointAuthMethod"; + private static final String TOS_URI = "tosUri"; + private static final String CONTACTS = "contacts"; + private static final String LOGO_URI = "logoUri"; + private static final String REDIRECT_URIS = "redirectUris"; + private static final String REFRESH_TOKEN_VALIDITY_SECONDS = "refreshTokenValiditySeconds"; + private static final String ACCESS_TOKEN_VALIDITY_SECONDS = "accessTokenValiditySeconds"; + private static final String SECRET = "secret"; + private static final String URI = "uri"; + private static final String CREATOR_USER_ID = "creatorUserId"; + private static final String APPROVED_ACCESS_TOKENS = "approvedAccessTokens"; + private static final String ALLOWED_SCOPES = "allowedScopes"; + private static final String USER_ID = "userId"; + private static final String TIMEOUT_DATE = "timeoutDate"; + private static final String CREATION_DATE = "creationDate"; + private static final String ACCESS_DATE = "accessDate"; + private static final String AUTHENTICATED = "authenticated"; + private static final String SOURCE_CLASS = "sourceClass"; + private static final String NAME = "name"; + private static final String SAVED_USER_AUTHENTICATION = "savedUserAuthentication"; + private static final String EXTENSIONS = "extensions"; + private static final String RESPONSE_TYPES = "responseTypes"; + private static final String REDIRECT_URI = "redirectUri"; + private static final String APPROVED = "approved"; + private static final String AUTHORITIES = "authorities"; + private static final String RESOURCE_IDS = "resourceIds"; + private static final String REQUEST_PARAMETERS = "requestParameters"; + private static final String TYPE = "type"; + private static final String SCOPE = "scope"; + private static final String ID_TOKEN_ID = "idTokenId"; + private static final String REFRESH_TOKEN_ID = "refreshTokenId"; + private static final String VALUE = "value"; + private static final String AUTHENTICATION_HOLDER_ID = "authenticationHolderId"; + private static final String CLIENT_ID = "clientId"; + private static final String EXPIRATION = "expiration"; + private static final String ID = "id"; + private static final String ICON_URI = "iconUri"; + private static final String OWNER = "owner"; + private static final String POLICIES = "policies"; + private static final String SCOPES = "scopes"; + private static final String CLAIMS_REQUIRED = "claimsRequired"; + private static final String ISSUER = "issuer"; + private static final String CLAIM_TOKEN_FORMAT = "claimTokenFormat"; + private static final String CLAIM_TYPE = "claimType"; + private static final String FRIENDLY_NAME = "friendlyName"; + private static final String PERMISSIONS = "permissions"; + private static final String RESOURCE_SET = "resourceSet"; + private static final String PERMISSION_TICKETS = "permissionTickets"; + private static final String PERMISSION = "permission"; + private static final String TICKET = "ticket"; + private static final String CLAIMS_SUPPLIED = "claimsSupplied"; + private static final String SAVED_REGISTERED_CLIENTS = "savedRegisteredClients"; + private static final String RESOURCE_SETS = "resourceSets"; + + private static final Logger logger = LoggerFactory.getLogger(UmaDataServiceExtension_1_3.class); + + @Autowired + private SavedRegisteredClientService registeredClientService; + @Autowired + private ResourceSetRepository resourceSetRepository; + @Autowired + private PermissionRepository permissionRepository; + + /* (non-Javadoc) + * @see org.mitre.openid.connect.service.MITREidDataServiceExtension#supportsVersion(java.lang.String) + */ + @Override + public boolean supportsVersion(String version) { + return THIS_VERSION.equals(version); + + } + + /* (non-Javadoc) + * @see org.mitre.openid.connect.service.MITREidDataServiceExtension#exportExtensionData(com.google.gson.stream.JsonWriter) + */ + @Override + public void exportExtensionData(JsonWriter writer) throws IOException { + writer.name(SAVED_REGISTERED_CLIENTS); + writer.beginArray(); + writeSavedRegisteredClients(writer); + writer.endArray(); + + writer.name(RESOURCE_SETS); + writer.beginArray(); + writeResourceSets(writer); + writer.endArray(); + + writer.name(PERMISSION_TICKETS); + writer.beginArray(); + writePermissionTickets(writer); + writer.endArray(); + } + + /** + * @param writer + * @throws IOException + */ + private void writePermissionTickets(JsonWriter writer) throws IOException { + for (PermissionTicket ticket : permissionRepository.getAll()) { + writer.beginObject(); + + writer.name(CLAIMS_SUPPLIED); + writer.beginArray(); + for (Claim claim : ticket.getClaimsSupplied()) { + writer.beginObject(); + + writer.name(ISSUER); + writer.beginArray(); + for (String issuer : claim.getIssuer()) { + writer.value(issuer); + } + writer.endArray(); + writer.name(CLAIM_TOKEN_FORMAT); + writer.beginArray(); + for (String format : claim.getClaimTokenFormat()) { + writer.value(format); + } + writer.endArray(); + writer.name(CLAIM_TYPE).value(claim.getClaimType()); + writer.name(FRIENDLY_NAME).value(claim.getFriendlyName()); + writer.name(NAME).value(claim.getName()); + writer.name(VALUE).value(claim.getValue().toString()); + writer.endObject(); + } + writer.endArray(); + + writer.name(EXPIRATION).value(toUTCString(ticket.getExpiration())); + + writer.name(PERMISSION); + writer.beginObject(); + Permission p = ticket.getPermission(); + writer.name(RESOURCE_SET).value(p.getResourceSet().getId()); + writer.name(SCOPES); + writer.beginArray(); + for (String s : p.getScopes()) { + writer.value(s); + } + writer.endArray(); + writer.endObject(); + + writer.name(TICKET).value(ticket.getTicket()); + + writer.endObject(); + } + + + } + + /** + * @param writer + * @throws IOException + */ + private void writeResourceSets(JsonWriter writer) throws IOException { + for (ResourceSet rs : resourceSetRepository.getAll()) { + writer.beginObject(); + writer.name(ID).value(rs.getId()); + writer.name(CLIENT_ID).value(rs.getClientId()); + writer.name(ICON_URI).value(rs.getIconUri()); + writer.name(NAME).value(rs.getName()); + writer.name(TYPE).value(rs.getType()); + writer.name(URI).value(rs.getUri()); + writer.name(OWNER).value(rs.getOwner()); + writer.name(POLICIES); + writer.beginArray(); + for (Policy policy : rs.getPolicies()) { + writer.beginObject(); + writer.name(NAME).value(policy.getName()); + writer.name(SCOPES); + writer.beginArray(); + for (String scope : policy.getScopes()) { + writer.value(scope); + } + writer.endArray(); + writer.name(CLAIMS_REQUIRED); + writer.beginArray(); + for (Claim claim : policy.getClaimsRequired()) { + writer.beginObject(); + + writer.name(ISSUER); + writer.beginArray(); + for (String issuer : claim.getIssuer()) { + writer.value(issuer); + } + writer.endArray(); + writer.name(CLAIM_TOKEN_FORMAT); + writer.beginArray(); + for (String format : claim.getClaimTokenFormat()) { + writer.value(format); + } + writer.endArray(); + writer.name(CLAIM_TYPE).value(claim.getClaimType()); + writer.name(FRIENDLY_NAME).value(claim.getFriendlyName()); + writer.name(NAME).value(claim.getName()); + writer.name(VALUE).value(claim.getValue().toString()); + writer.endObject(); + } + writer.endArray(); + writer.endObject(); + } + writer.endArray(); + writer.name(SCOPES); + writer.beginArray(); + for (String scope : rs.getScopes()) { + writer.value(scope); + } + writer.endArray(); + writer.endObject(); + logger.debug("Finished writing resource set {}", rs.getId()); + } + + } + + /** + * @param writer + */ + private void writeSavedRegisteredClients(JsonWriter writer) throws IOException { + for (SavedRegisteredClient src : registeredClientService.getAll()) { + writer.beginObject(); + writer.name(ISSUER).value(src.getIssuer()); + writer.name(REGISTERED_CLIENT).value(src.getRegisteredClient().getSource().toString()); + writer.endObject(); + logger.debug("Wrote saved registered client {}", src.getId()); + } + logger.info("Done writing saved registered clients"); + } + + /* (non-Javadoc) + * @see org.mitre.openid.connect.service.MITREidDataServiceExtension#importExtensionData(com.google.gson.stream.JsonReader) + */ + @Override + public boolean importExtensionData(String name, JsonReader reader) throws IOException { + if (name.equals(SAVED_REGISTERED_CLIENTS)) { + readSavedRegisteredClients(reader); + return true; + } else if (name.equals(RESOURCE_SETS)) { + readResourceSets(reader); + return true; + } else if (name.equals(PERMISSION_TICKETS)) { + readPermissionTickets(reader); + return true; + } else { + return false; + } + } + + private Map permissionToResourceRefs = new HashMap<>(); + + /** + * @param reader + */ + private void readPermissionTickets(JsonReader reader) throws IOException { + JsonParser parser = new JsonParser(); + reader.beginArray(); + while (reader.hasNext()) { + PermissionTicket ticket = new PermissionTicket(); + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(CLAIMS_SUPPLIED)) { + Set claimsSupplied = new HashSet<>(); + reader.beginArray(); + while (reader.hasNext()) { + Claim c = new Claim(); + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String cname = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (cname.equals(ISSUER)) { + c.setIssuer(readSet(reader)); + } else if (cname.equals(CLAIM_TOKEN_FORMAT)) { + c.setClaimTokenFormat(readSet(reader)); + } else if (cname.equals(CLAIM_TYPE)) { + c.setClaimType(reader.nextString()); + } else if (cname.equals(FRIENDLY_NAME)) { + c.setFriendlyName(reader.nextString()); + } else if (cname.equals(NAME)) { + c.setName(reader.nextString()); + } else if (cname.equals(VALUE)) { + JsonElement e = parser.parse(reader.nextString()); + c.setValue(e); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + claimsSupplied.add(c); + } + reader.endArray(); + ticket.setClaimsSupplied(claimsSupplied); + } else if (name.equals(EXPIRATION)) { + ticket.setExpiration(utcToDate(reader.nextString())); + } else if (name.equals(PERMISSION)) { + Permission p = new Permission(); + Long rsid = null; + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String pname = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (pname.equals(RESOURCE_SET)) { + rsid = reader.nextLong(); + } else if (pname.equals(SCOPES)) { + p.setScopes(readSet(reader)); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + Permission saved = permissionRepository.saveRawPermission(p); + permissionToResourceRefs.put(saved.getId(), rsid); + ticket.setPermission(saved); + } else if (name.equals(TICKET)) { + ticket.setTicket(reader.nextString()); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + permissionRepository.save(ticket); + } + reader.endArray(); + } + + + private Map resourceSetOldToNewIdMap = new HashMap<>(); + + /** + * @param reader + */ + private void readResourceSets(JsonReader reader) throws IOException { + JsonParser parser = new JsonParser(); + reader.beginArray(); + while (reader.hasNext()) { + Long oldId = null; + ResourceSet rs = new ResourceSet(); + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ID)) { + oldId = reader.nextLong(); + } else if (name.equals(CLIENT_ID)) { + rs.setClientId(reader.nextString()); + } else if (name.equals(ICON_URI)) { + rs.setIconUri(reader.nextString()); + } else if (name.equals(NAME)) { + rs.setName(reader.nextString()); + } else if (name.equals(TYPE)) { + rs.setType(reader.nextString()); + } else if (name.equals(URI)) { + rs.setUri(reader.nextString()); + } else if (name.equals(OWNER)) { + rs.setOwner(reader.nextString()); + } else if (name.equals(POLICIES)) { + Set policies = new HashSet<>(); + reader.beginArray(); + while (reader.hasNext()) { + Policy p = new Policy(); + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String pname = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (pname.equals(NAME)) { + p.setName(reader.nextString()); + } else if (pname.equals(SCOPES)) { + p.setScopes(readSet(reader)); + } else if (pname.equals(CLAIMS_REQUIRED)) { + Set claimsRequired = new HashSet<>(); + reader.beginArray(); + while (reader.hasNext()) { + Claim c = new Claim(); + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String cname = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (cname.equals(ISSUER)) { + c.setIssuer(readSet(reader)); + } else if (cname.equals(CLAIM_TOKEN_FORMAT)) { + c.setClaimTokenFormat(readSet(reader)); + } else if (cname.equals(CLAIM_TYPE)) { + c.setClaimType(reader.nextString()); + } else if (cname.equals(FRIENDLY_NAME)) { + c.setFriendlyName(reader.nextString()); + } else if (cname.equals(NAME)) { + c.setName(reader.nextString()); + } else if (cname.equals(VALUE)) { + JsonElement e = parser.parse(reader.nextString()); + c.setValue(e); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + claimsRequired.add(c); + } + reader.endArray(); + p.setClaimsRequired(claimsRequired); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + policies.add(p); + } + reader.endArray(); + rs.setPolicies(policies); + } else if (name.equals(SCOPES)) { + rs.setScopes(readSet(reader)); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + Long newId = resourceSetRepository.save(rs).getId(); + resourceSetOldToNewIdMap.put(oldId, newId); + } + reader.endArray(); + logger.info("Done reading resource sets"); + } + + /** + * @param reader + */ + private void readSavedRegisteredClients(JsonReader reader) throws IOException{ + reader.beginArray(); + while (reader.hasNext()) { + String issuer = null; + String clientString = null; + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ISSUER)) { + issuer = reader.nextString(); + } else if (name.equals(REGISTERED_CLIENT)) { + clientString = reader.nextString(); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + RegisteredClient client = ClientDetailsEntityJsonProcessor.parseRegistered(clientString); + registeredClientService.save(issuer, client); + logger.debug("Saved registered client"); + } + reader.endArray(); + logger.info("Done reading saved registered clients"); + } + + /* (non-Javadoc) + * @see org.mitre.openid.connect.service.MITREidDataServiceExtension#fixExtensionObjectReferences() + */ + @Override + public void fixExtensionObjectReferences() { + for (Long permissionId : permissionToResourceRefs.keySet()) { + Long oldResourceId = permissionToResourceRefs.get(permissionId); + Long newResourceId = resourceSetOldToNewIdMap.get(oldResourceId); + Permission p = permissionRepository.getById(permissionId); + ResourceSet rs = resourceSetRepository.getById(newResourceId); + p.setResourceSet(rs); + permissionRepository.saveRawPermission(p); + logger.debug("Mapping rsid " + oldResourceId + " to " + newResourceId + " for permission " + permissionId); + } + permissionToResourceRefs.clear(); + resourceSetOldToNewIdMap.clear(); + } + +} From 3acb71763ab54a68931ae318efd3270a006c5ab3 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 16 Feb 2017 18:23:32 -0500 Subject: [PATCH 118/269] cleaned up UMA server overlay --- .../webapp/WEB-INF/application-context.xml | 8 +- .../main/webapp/WEB-INF/endpoint-config.xml | 35 +++ .../src/main/resources/db/hsql/scopes.sql | 22 +- .../webapp/WEB-INF/application-context.xml | 282 ------------------ .../main/webapp/WEB-INF/endpoint-config.xml | 54 ++++ .../src/main/webapp/WEB-INF/server-config.xml | 10 +- .../src/main/webapp/resources/js/admin.js | 117 +++++--- 7 files changed, 183 insertions(+), 345 deletions(-) create mode 100644 openid-connect-server-webapp/src/main/webapp/WEB-INF/endpoint-config.xml delete mode 100644 uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml create mode 100644 uma-server-webapp/src/main/webapp/WEB-INF/endpoint-config.xml diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml index b750749584..0727daf120 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml @@ -144,7 +144,7 @@ create-session="stateless" authentication-manager-ref="clientAuthenticationManager"> - + @@ -157,7 +157,7 @@ create-session="stateless" authentication-manager-ref="clientAuthenticationManager"> - + @@ -170,6 +170,10 @@ + + + + diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/endpoint-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/endpoint-config.xml new file mode 100644 index 0000000000..6f3e96f072 --- /dev/null +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/endpoint-config.xml @@ -0,0 +1,35 @@ + + + + + + + + diff --git a/uma-server-webapp/src/main/resources/db/hsql/scopes.sql b/uma-server-webapp/src/main/resources/db/hsql/scopes.sql index 9b0446af51..c3ea0b1133 100755 --- a/uma-server-webapp/src/main/resources/db/hsql/scopes.sql +++ b/uma-server-webapp/src/main/resources/db/hsql/scopes.sql @@ -10,25 +10,25 @@ START TRANSACTION; -- Insert scope information into the temporary tables. -- -INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES - ('openid', 'log in using your identity', 'user', false, true, false, null), - ('profile', 'basic profile information', 'list-alt', false, true, false, null), - ('email', 'email address', 'envelope', false, true, false, null), - ('address', 'physical address', 'home', false, true, false, null), - ('phone', 'telephone number', 'bell', false, true, false, null), - ('offline_access', 'offline access', 'time', false, false, false, null), - ('uma_protection', 'manage protected resources', 'briefcase', false, false, false, null), - ('uma_authorization', 'request access to protected resources', 'share', false, false, false, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope) VALUES + ('openid', 'log in using your identity', 'user', false, true), + ('profile', 'basic profile information', 'list-alt', false, true), + ('email', 'email address', 'envelope', false, true), + ('address', 'physical address', 'home', false, true), + ('phone', 'telephone number', 'bell', false, true), + ('offline_access', 'offline access', 'time', false, false), + ('uma_protection', 'manage protected resources', 'briefcase', false, false), + ('uma_authorization', 'request access to protected resources', 'share', false, false); -- -- Merge the temporary scopes safely into the database. This is a two-step process to keep scopes from being created on every startup with a persistent store. -- MERGE INTO system_scope - USING (SELECT scope, description, icon, restricted, default_scope, structured, structured_param_description FROM system_scope_TEMP) AS vals(scope, description, icon, restricted, default_scope, structured, structured_param_description) + USING (SELECT scope, description, icon, restricted, default_scope FROM system_scope_TEMP) AS vals(scope, description, icon, restricted, default_scope) ON vals.scope = system_scope.scope WHEN NOT MATCHED THEN - INSERT (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES(vals.scope, vals.description, vals.icon, vals.restricted, vals.default_scope, vals.structured, vals.structured_param_description); + INSERT (scope, description, icon, restricted, default_scope) VALUES(vals.scope, vals.description, vals.icon, vals.restricted, vals.default_scope); COMMIT; diff --git a/uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml b/uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml deleted file mode 100644 index fe2f28a3dc..0000000000 --- a/uma-server-webapp/src/main/webapp/WEB-INF/application-context.xml +++ /dev/null @@ -1,282 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /introspect - /revoke - /token - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/uma-server-webapp/src/main/webapp/WEB-INF/endpoint-config.xml b/uma-server-webapp/src/main/webapp/WEB-INF/endpoint-config.xml new file mode 100644 index 0000000000..dc4cb15cf0 --- /dev/null +++ b/uma-server-webapp/src/main/webapp/WEB-INF/endpoint-config.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uma-server-webapp/src/main/webapp/WEB-INF/server-config.xml b/uma-server-webapp/src/main/webapp/WEB-INF/server-config.xml index afaadc309e..f4ea0603b6 100644 --- a/uma-server-webapp/src/main/webapp/WEB-INF/server-config.xml +++ b/uma-server-webapp/src/main/webapp/WEB-INF/server-config.xml @@ -43,7 +43,7 @@ - + @@ -56,6 +56,14 @@ messages + + + + + + + diff --git a/uma-server-webapp/src/main/webapp/resources/js/admin.js b/uma-server-webapp/src/main/webapp/resources/js/admin.js index 0020d3dc6e..c21e3a1430 100644 --- a/uma-server-webapp/src/main/webapp/resources/js/admin.js +++ b/uma-server-webapp/src/main/webapp/resources/js/admin.js @@ -94,22 +94,7 @@ var ListWidgetChildView = Backbone.View.extend({ this.model.destroy({ dataType: false, processData: false, - error:function (error, response) { - console.log("An error occurred when deleting from a list widget"); - - //Pull out the response text. - var responseJson = JSON.parse(response.responseText); - - //Display an alert with an error message - $('#modalAlert div.modal-header').html(responseJson.error); - $('#modalAlert div.modal-body').html(responseJson.error_description); - - $("#modalAlert").modal({ // wire up the actual modal functionality and show the dialog - "backdrop" : "static", - "keyboard" : true, - "show" : true // ensure the modal is shown immediately - }); - } + error:app.errorHandlerView.handleError() }); }, @@ -390,14 +375,14 @@ var ErrorHandlerView = Backbone.View.extend({ message = {}; } - if (message.log) { - console.log(message.log); - } - var _self = this; return function(model, response, options) { - + + if (message.log) { + console.log(message.log); + } + _self.showErrorMessage( _self.headerTemplate({message: message, model: model, response: response, options: options}), _self.template({message: message, model: model, response: response, options: options}) @@ -406,8 +391,8 @@ var ErrorHandlerView = Backbone.View.extend({ $('#modalAlert .modal-body .page-reload').on('click', _self.reloadPage); } - }, - + }, + showErrorMessage:function(header, message) { // hide the sheet if it's visible $('#loadingbox').sheet('hide'); @@ -554,21 +539,42 @@ var AppRouter = Backbone.Router.extend({ contacts.push(userInfo.email); } - client.set({ - tokenEndpointAuthMethod: "SECRET_BASIC", - generateClientSecret:true, - displayClientSecret:false, - requireAuthTime:true, - defaultMaxAge:60000, - scope: _.uniq(_.flatten(app.systemScopeList.defaultScopes().pluck("value"))), - accessTokenValiditySeconds:3600, - idTokenValiditySeconds:600, - grantTypes: ["authorization_code"], - responseTypes: ["code"], - subjectType: "PUBLIC", - jwksType: "URI", - contacts: contacts - }, { silent: true }); + // use a different set of defaults based on heart mode flag + if (heartMode) { + client.set({ + tokenEndpointAuthMethod: "PRIVATE_KEY", + generateClientSecret:false, + displayClientSecret:false, + requireAuthTime:true, + defaultMaxAge:60000, + scope: _.uniq(_.flatten(app.systemScopeList.defaultScopes().pluck("value"))), + accessTokenValiditySeconds:3600, + refreshTokenValiditySeconds:24*3600, + idTokenValiditySeconds:300, + grantTypes: ["authorization_code"], + responseTypes: ["code"], + subjectType: "PUBLIC", + jwksType: "URI", + contacts: contacts + }, { silent: true }); + } else { + // set up this new client to require a secret and have us autogenerate one + client.set({ + tokenEndpointAuthMethod: "SECRET_BASIC", + generateClientSecret:true, + displayClientSecret:false, + requireAuthTime:true, + defaultMaxAge:60000, + scope: _.uniq(_.flatten(app.systemScopeList.defaultScopes().pluck("value"))), + accessTokenValiditySeconds:3600, + idTokenValiditySeconds:600, + grantTypes: ["authorization_code"], + responseTypes: ["code"], + subjectType: "PUBLIC", + jwksType: "URI", + contacts: contacts + }, { silent: true }); + } $('#content').html(view.render().el); @@ -921,17 +927,30 @@ var AppRouter = Backbone.Router.extend({ contacts.push(userInfo.email); } - client.set({ - require_auth_time:true, - default_max_age:60000, - scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), - token_endpoint_auth_method: 'client_secret_basic', - grant_types: ["authorization_code"], - response_types: ["code"], - subject_type: "public", - contacts: contacts - }, { silent: true }); - + if (heartMode) { + client.set({ + require_auth_time:true, + default_max_age:60000, + scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), + token_endpoint_auth_method: 'private_key_jwt', + grant_types: ["authorization_code"], + response_types: ["code"], + subject_type: "public", + contacts: contacts + }, { silent: true }); + } else { + client.set({ + require_auth_time:true, + default_max_age:60000, + scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), + token_endpoint_auth_method: 'client_secret_basic', + grant_types: ["authorization_code"], + response_types: ["code"], + subject_type: "public", + contacts: contacts + }, { silent: true }); + } + $('#content').html(view.render().el); view.delegateEvents(); setPageTitle($.t('dynreg.new-client')); From 8178af87f0277001eea0bb2e5941442773a715d8 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 16 Feb 2017 18:24:05 -0500 Subject: [PATCH 119/269] further modularized data import/export service --- .../service/MITREidDataServiceExtension.java | 2 +- .../service/MITREidDataServiceMaps.java | 119 ++ .../service/impl/MITREidDataService_1_0.java | 102 +- .../service/impl/MITREidDataService_1_1.java | 101 +- .../service/impl/MITREidDataService_1_2.java | 102 +- .../service/impl/MITREidDataService_1_3.java | 121 +- .../service/impl/MITREidDataService_1_2.java | 1815 ----------------- .../impl/UmaDataServiceExtension_1_3.java | 137 +- 8 files changed, 434 insertions(+), 2065 deletions(-) create mode 100644 openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceMaps.java delete mode 100644 uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceExtension.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceExtension.java index eff90288e8..98ddd027f4 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceExtension.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceExtension.java @@ -56,7 +56,7 @@ public interface MITREidDataServiceExtension { /** * Signal the extension to wrap up all object processing and finalize its */ - void fixExtensionObjectReferences(); + void fixExtensionObjectReferences(MITREidDataServiceMaps maps); /** * Return diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceMaps.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceMaps.java new file mode 100644 index 0000000000..27430c3b24 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceMaps.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.openid.connect.service; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * @author jricher + * + */ +public class MITREidDataServiceMaps { + + private Map accessTokenOldToNewIdMap = new HashMap(); + private Map accessTokenToAuthHolderRefs = new HashMap(); + private Map accessTokenToClientRefs = new HashMap(); + private Map accessTokenToRefreshTokenRefs = new HashMap(); + private Map authHolderOldToNewIdMap = new HashMap(); + private Map grantOldToNewIdMap = new HashMap<>(); + private Map> grantToAccessTokensRefs = new HashMap<>(); + private Map refreshTokenOldToNewIdMap = new HashMap(); + private Map refreshTokenToAuthHolderRefs = new HashMap(); + private Map refreshTokenToClientRefs = new HashMap(); + private Map whitelistedSiteOldToNewIdMap = new HashMap(); + /** + * @return the accessTokenOldToNewIdMap + */ + public Map getAccessTokenOldToNewIdMap() { + return accessTokenOldToNewIdMap; + } + /** + * @return the accessTokenToAuthHolderRefs + */ + public Map getAccessTokenToAuthHolderRefs() { + return accessTokenToAuthHolderRefs; + } + /** + * @return the accessTokenToClientRefs + */ + public Map getAccessTokenToClientRefs() { + return accessTokenToClientRefs; + } + /** + * @return the accessTokenToRefreshTokenRefs + */ + public Map getAccessTokenToRefreshTokenRefs() { + return accessTokenToRefreshTokenRefs; + } + /** + * @return the authHolderOldToNewIdMap + */ + public Map getAuthHolderOldToNewIdMap() { + return authHolderOldToNewIdMap; + } + /** + * @return the grantOldToNewIdMap + */ + public Map getGrantOldToNewIdMap() { + return grantOldToNewIdMap; + } + /** + * @return the grantToAccessTokensRefs + */ + public Map> getGrantToAccessTokensRefs() { + return grantToAccessTokensRefs; + } + /** + * @return the refreshTokenOldToNewIdMap + */ + public Map getRefreshTokenOldToNewIdMap() { + return refreshTokenOldToNewIdMap; + } + /** + * @return the refreshTokenToAuthHolderRefs + */ + public Map getRefreshTokenToAuthHolderRefs() { + return refreshTokenToAuthHolderRefs; + } + /** + * @return the refreshTokenToClientRefs + */ + public Map getRefreshTokenToClientRefs() { + return refreshTokenToClientRefs; + } + /** + * @return the whitelistedSiteOldToNewIdMap + */ + public Map getWhitelistedSiteOldToNewIdMap() { + return whitelistedSiteOldToNewIdMap; + } + + public void clearAll() { + refreshTokenToClientRefs.clear(); + refreshTokenToAuthHolderRefs.clear(); + accessTokenToClientRefs.clear(); + accessTokenToAuthHolderRefs.clear(); + accessTokenToRefreshTokenRefs.clear(); + refreshTokenOldToNewIdMap.clear(); + accessTokenOldToNewIdMap.clear(); + grantOldToNewIdMap.clear(); + } + +} diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java index 0560032d74..c295836268 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java @@ -49,6 +49,7 @@ import org.mitre.openid.connect.repository.WhitelistedSiteRepository; import org.mitre.openid.connect.service.MITREidDataService; import org.mitre.openid.connect.service.MITREidDataServiceExtension; +import org.mitre.openid.connect.service.MITREidDataServiceMaps; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -102,6 +103,8 @@ public class MITREidDataService_1_0 extends MITREidDataServiceSupport implements @Autowired(required = false) private List extensions = Collections.emptyList(); + private MITREidDataServiceMaps maps = new MITREidDataServiceMaps(); + private static final String THIS_VERSION = MITREID_CONNECT_1_0; @Override @@ -154,8 +157,8 @@ public void importData(JsonReader reader) throws IOException { } else { for (MITREidDataServiceExtension extension : extensions) { if (extension.supportsVersion(THIS_VERSION)) { - if (extension.canImport(name)) { - extension.importExtensionData(reader); + if (extension.supportsVersion(THIS_VERSION)) { + extension.importExtensionData(name, reader); break; } } @@ -176,15 +179,12 @@ public void importData(JsonReader reader) throws IOException { fixObjectReferences(); for (MITREidDataServiceExtension extension : extensions) { if (extension.supportsVersion(THIS_VERSION)) { - extension.fixExtensionObjectReferences(); + extension.fixExtensionObjectReferences(maps); break; } } + maps.clearAll(); } - private Map refreshTokenToClientRefs = new HashMap<>(); - private Map refreshTokenToAuthHolderRefs = new HashMap<>(); - private Map refreshTokenOldToNewIdMap = new HashMap<>(); - /** * @param reader * @throws IOException @@ -238,19 +238,14 @@ private void readRefreshTokens(JsonReader reader) throws IOException { } reader.endObject(); Long newId = tokenRepository.saveRefreshToken(token).getId(); - refreshTokenToClientRefs.put(currentId, clientId); - refreshTokenToAuthHolderRefs.put(currentId, authHolderId); - refreshTokenOldToNewIdMap.put(currentId, newId); + maps.getRefreshTokenToClientRefs().put(currentId, clientId); + maps.getRefreshTokenToAuthHolderRefs().put(currentId, authHolderId); + maps.getRefreshTokenOldToNewIdMap().put(currentId, newId); logger.debug("Read refresh token {}", currentId); } reader.endArray(); logger.info("Done reading refresh tokens"); } - private Map accessTokenToClientRefs = new HashMap<>(); - private Map accessTokenToAuthHolderRefs = new HashMap<>(); - private Map accessTokenToRefreshTokenRefs = new HashMap<>(); - private Map accessTokenOldToNewIdMap = new HashMap<>(); - /** * @param reader * @throws IOException @@ -313,19 +308,17 @@ private void readAccessTokens(JsonReader reader) throws IOException { } reader.endObject(); Long newId = tokenRepository.saveAccessToken(token).getId(); - accessTokenToClientRefs.put(currentId, clientId); - accessTokenToAuthHolderRefs.put(currentId, authHolderId); + maps.getAccessTokenToClientRefs().put(currentId, clientId); + maps.getAccessTokenToAuthHolderRefs().put(currentId, authHolderId); if (refreshTokenId != null) { - accessTokenToRefreshTokenRefs.put(currentId, refreshTokenId); + maps.getAccessTokenToRefreshTokenRefs().put(currentId, refreshTokenId); } - accessTokenOldToNewIdMap.put(currentId, newId); + maps.getAccessTokenOldToNewIdMap().put(currentId, newId); logger.debug("Read access token {}", currentId); } reader.endArray(); logger.info("Done reading access tokens"); } - private Map authHolderOldToNewIdMap = new HashMap<>(); - /** * @param reader * @throws IOException @@ -397,7 +390,7 @@ private void readAuthenticationHolders(JsonReader reader) throws IOException { } reader.endObject(); Long newId = authHolderRepository.save(ahe).getId(); - authHolderOldToNewIdMap.put(currentId, newId); + maps.getAuthHolderOldToNewIdMap().put(currentId, newId); logger.debug("Read authentication holder {}", currentId); } reader.endArray(); @@ -511,9 +504,6 @@ private SavedUserAuthentication readSavedUserAuthentication(JsonReader reader) t return savedUserAuth; } - Map grantOldToNewIdMap = new HashMap<>(); - Map> grantToAccessTokensRefs = new HashMap<>(); - /** * @param reader * @throws IOException @@ -569,20 +559,18 @@ private void readGrants(JsonReader reader) throws IOException { } reader.endObject(); Long newId = approvedSiteRepository.save(site).getId(); - grantOldToNewIdMap.put(currentId, newId); + maps.getGrantOldToNewIdMap().put(currentId, newId); if (whitelistedSiteId != null) { logger.debug("Ignoring whitelisted site marker on approved site."); } if (tokenIds != null) { - grantToAccessTokensRefs.put(currentId, tokenIds); + maps.getGrantToAccessTokensRefs().put(currentId, tokenIds); } logger.debug("Read grant {}", currentId); } reader.endArray(); logger.info("Done reading grants"); } - Map whitelistedSiteOldToNewIdMap = new HashMap<>(); - /** * @param reader * @throws IOException @@ -621,7 +609,7 @@ private void readWhitelistedSites(JsonReader reader) throws IOException { } reader.endObject(); Long newId = wlSiteRepository.save(wlSite).getId(); - whitelistedSiteOldToNewIdMap.put(currentId, newId); + maps.getWhitelistedSiteOldToNewIdMap().put(currentId, newId); } reader.endArray(); logger.info("Done reading whitelisted sites"); @@ -853,64 +841,58 @@ private void readSystemScopes(JsonReader reader) throws IOException { } private void fixObjectReferences() { - for (Long oldRefreshTokenId : refreshTokenToClientRefs.keySet()) { - String clientRef = refreshTokenToClientRefs.get(oldRefreshTokenId); + for (Long oldRefreshTokenId : maps.getRefreshTokenToClientRefs().keySet()) { + String clientRef = maps.getRefreshTokenToClientRefs().get(oldRefreshTokenId); ClientDetailsEntity client = clientRepository.getClientByClientId(clientRef); - Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); + Long newRefreshTokenId = maps.getRefreshTokenOldToNewIdMap().get(oldRefreshTokenId); OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); refreshToken.setClient(client); tokenRepository.saveRefreshToken(refreshToken); } - refreshTokenToClientRefs.clear(); - for (Long oldRefreshTokenId : refreshTokenToAuthHolderRefs.keySet()) { - Long oldAuthHolderId = refreshTokenToAuthHolderRefs.get(oldRefreshTokenId); - Long newAuthHolderId = authHolderOldToNewIdMap.get(oldAuthHolderId); + for (Long oldRefreshTokenId : maps.getRefreshTokenToAuthHolderRefs().keySet()) { + Long oldAuthHolderId = maps.getRefreshTokenToAuthHolderRefs().get(oldRefreshTokenId); + Long newAuthHolderId = maps.getAuthHolderOldToNewIdMap().get(oldAuthHolderId); AuthenticationHolderEntity authHolder = authHolderRepository.getById(newAuthHolderId); - Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); + Long newRefreshTokenId = maps.getRefreshTokenOldToNewIdMap().get(oldRefreshTokenId); OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); refreshToken.setAuthenticationHolder(authHolder); tokenRepository.saveRefreshToken(refreshToken); } - refreshTokenToAuthHolderRefs.clear(); - for (Long oldAccessTokenId : accessTokenToClientRefs.keySet()) { - String clientRef = accessTokenToClientRefs.get(oldAccessTokenId); + for (Long oldAccessTokenId : maps.getAccessTokenToClientRefs().keySet()) { + String clientRef = maps.getAccessTokenToClientRefs().get(oldAccessTokenId); ClientDetailsEntity client = clientRepository.getClientByClientId(clientRef); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + Long newAccessTokenId = maps.getAccessTokenOldToNewIdMap().get(oldAccessTokenId); OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); accessToken.setClient(client); tokenRepository.saveAccessToken(accessToken); } - accessTokenToClientRefs.clear(); - for (Long oldAccessTokenId : accessTokenToAuthHolderRefs.keySet()) { - Long oldAuthHolderId = accessTokenToAuthHolderRefs.get(oldAccessTokenId); - Long newAuthHolderId = authHolderOldToNewIdMap.get(oldAuthHolderId); + for (Long oldAccessTokenId : maps.getAccessTokenToAuthHolderRefs().keySet()) { + Long oldAuthHolderId = maps.getAccessTokenToAuthHolderRefs().get(oldAccessTokenId); + Long newAuthHolderId = maps.getAuthHolderOldToNewIdMap().get(oldAuthHolderId); AuthenticationHolderEntity authHolder = authHolderRepository.getById(newAuthHolderId); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + Long newAccessTokenId = maps.getAccessTokenOldToNewIdMap().get(oldAccessTokenId); OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); accessToken.setAuthenticationHolder(authHolder); tokenRepository.saveAccessToken(accessToken); } - accessTokenToAuthHolderRefs.clear(); - for (Long oldAccessTokenId : accessTokenToRefreshTokenRefs.keySet()) { - Long oldRefreshTokenId = accessTokenToRefreshTokenRefs.get(oldAccessTokenId); - Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); + maps.getAccessTokenToAuthHolderRefs().clear(); + for (Long oldAccessTokenId : maps.getAccessTokenToRefreshTokenRefs().keySet()) { + Long oldRefreshTokenId = maps.getAccessTokenToRefreshTokenRefs().get(oldAccessTokenId); + Long newRefreshTokenId = maps.getRefreshTokenOldToNewIdMap().get(oldRefreshTokenId); OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + Long newAccessTokenId = maps.getAccessTokenOldToNewIdMap().get(oldAccessTokenId); OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); accessToken.setRefreshToken(refreshToken); tokenRepository.saveAccessToken(accessToken); } - accessTokenToRefreshTokenRefs.clear(); - refreshTokenOldToNewIdMap.clear(); - whitelistedSiteOldToNewIdMap.clear(); - for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { - Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); + for (Long oldGrantId : maps.getGrantToAccessTokensRefs().keySet()) { + Set oldAccessTokenIds = maps.getGrantToAccessTokensRefs().get(oldGrantId); - Long newGrantId = grantOldToNewIdMap.get(oldGrantId); + Long newGrantId = maps.getGrantOldToNewIdMap().get(oldGrantId); ApprovedSite site = approvedSiteRepository.getById(newGrantId); for(Long oldTokenId : oldAccessTokenIds) { - Long newTokenId = accessTokenOldToNewIdMap.get(oldTokenId); + Long newTokenId = maps.getAccessTokenOldToNewIdMap().get(oldTokenId); OAuth2AccessTokenEntity token = tokenRepository.getAccessTokenById(newTokenId); token.setApprovedSite(site); tokenRepository.saveAccessToken(token); @@ -918,8 +900,6 @@ private void fixObjectReferences() { approvedSiteRepository.save(site); } - accessTokenOldToNewIdMap.clear(); - grantOldToNewIdMap.clear(); } } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java index ca976aac4a..0edc3aea2e 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java @@ -51,6 +51,7 @@ import org.mitre.openid.connect.repository.WhitelistedSiteRepository; import org.mitre.openid.connect.service.MITREidDataService; import org.mitre.openid.connect.service.MITREidDataServiceExtension; +import org.mitre.openid.connect.service.MITREidDataServiceMaps; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -107,6 +108,8 @@ public class MITREidDataService_1_1 extends MITREidDataServiceSupport implements private static final String THIS_VERSION = MITREID_CONNECT_1_1; + private MITREidDataServiceMaps maps = new MITREidDataServiceMaps(); + @Override public boolean supportsVersion(String version) { return THIS_VERSION.equals(version); @@ -156,8 +159,8 @@ public void importData(JsonReader reader) throws IOException { } else { for (MITREidDataServiceExtension extension : extensions) { if (extension.supportsVersion(THIS_VERSION)) { - if (extension.canImport(name)) { - extension.importExtensionData(reader); + if (extension.supportsVersion(THIS_VERSION)) { + extension.importExtensionData(name, reader); break; } } @@ -179,15 +182,12 @@ public void importData(JsonReader reader) throws IOException { fixObjectReferences(); for (MITREidDataServiceExtension extension : extensions) { if (extension.supportsVersion(THIS_VERSION)) { - extension.fixExtensionObjectReferences(); + extension.fixExtensionObjectReferences(maps); break; } } + maps.clearAll(); } - private Map refreshTokenToClientRefs = new HashMap<>(); - private Map refreshTokenToAuthHolderRefs = new HashMap<>(); - private Map refreshTokenOldToNewIdMap = new HashMap<>(); - /** * @param reader * @throws IOException @@ -241,19 +241,14 @@ private void readRefreshTokens(JsonReader reader) throws IOException { } reader.endObject(); Long newId = tokenRepository.saveRefreshToken(token).getId(); - refreshTokenToClientRefs.put(currentId, clientId); - refreshTokenToAuthHolderRefs.put(currentId, authHolderId); - refreshTokenOldToNewIdMap.put(currentId, newId); + maps.getRefreshTokenToClientRefs().put(currentId, clientId); + maps.getRefreshTokenToAuthHolderRefs().put(currentId, authHolderId); + maps.getRefreshTokenOldToNewIdMap().put(currentId, newId); logger.debug("Read refresh token {}", currentId); } reader.endArray(); logger.info("Done reading refresh tokens"); } - private Map accessTokenToClientRefs = new HashMap<>(); - private Map accessTokenToAuthHolderRefs = new HashMap<>(); - private Map accessTokenToRefreshTokenRefs = new HashMap<>(); - private Map accessTokenOldToNewIdMap = new HashMap<>(); - /** * @param reader * @throws IOException @@ -316,19 +311,17 @@ private void readAccessTokens(JsonReader reader) throws IOException { } reader.endObject(); Long newId = tokenRepository.saveAccessToken(token).getId(); - accessTokenToClientRefs.put(currentId, clientId); - accessTokenToAuthHolderRefs.put(currentId, authHolderId); + maps.getAccessTokenToClientRefs().put(currentId, clientId); + maps.getAccessTokenToAuthHolderRefs().put(currentId, authHolderId); if (refreshTokenId != null) { - accessTokenToRefreshTokenRefs.put(currentId, refreshTokenId); + maps.getAccessTokenToRefreshTokenRefs().put(currentId, refreshTokenId); } - accessTokenOldToNewIdMap.put(currentId, newId); + maps.getAccessTokenOldToNewIdMap().put(currentId, newId); logger.debug("Read access token {}", currentId); } reader.endArray(); logger.info("Done reading access tokens"); } - private Map authHolderOldToNewIdMap = new HashMap<>(); - /** * @param reader * @throws IOException @@ -400,7 +393,7 @@ private void readAuthenticationHolders(JsonReader reader) throws IOException { } reader.endObject(); Long newId = authHolderRepository.save(ahe).getId(); - authHolderOldToNewIdMap.put(currentId, newId); + maps.getAuthHolderOldToNewIdMap().put(currentId, newId); logger.debug("Read authentication holder {}", currentId); } reader.endArray(); @@ -521,9 +514,6 @@ private SavedUserAuthentication readSavedUserAuthentication(JsonReader reader) t return savedUserAuth; } - Map grantOldToNewIdMap = new HashMap<>(); - Map> grantToAccessTokensRefs = new HashMap<>(); - /** * @param reader * @throws IOException @@ -579,20 +569,18 @@ private void readGrants(JsonReader reader) throws IOException { } reader.endObject(); Long newId = approvedSiteRepository.save(site).getId(); - grantOldToNewIdMap.put(currentId, newId); + maps.getGrantOldToNewIdMap().put(currentId, newId); if (whitelistedSiteId != null) { logger.debug("Ignoring whitelisted site marker on approved site."); } if (tokenIds != null) { - grantToAccessTokensRefs.put(currentId, tokenIds); + maps.getGrantToAccessTokensRefs().put(currentId, tokenIds); } logger.debug("Read grant {}", currentId); } reader.endArray(); logger.info("Done reading grants"); } - Map whitelistedSiteOldToNewIdMap = new HashMap<>(); - /** * @param reader * @throws IOException @@ -631,7 +619,7 @@ private void readWhitelistedSites(JsonReader reader) throws IOException { } reader.endObject(); Long newId = wlSiteRepository.save(wlSite).getId(); - whitelistedSiteOldToNewIdMap.put(currentId, newId); + maps.getWhitelistedSiteOldToNewIdMap().put(currentId, newId); } reader.endArray(); logger.info("Done reading whitelisted sites"); @@ -867,63 +855,58 @@ private void readSystemScopes(JsonReader reader) throws IOException { } private void fixObjectReferences() { - for (Long oldRefreshTokenId : refreshTokenToClientRefs.keySet()) { - String clientRef = refreshTokenToClientRefs.get(oldRefreshTokenId); + for (Long oldRefreshTokenId : maps.getRefreshTokenToClientRefs().keySet()) { + String clientRef = maps.getRefreshTokenToClientRefs().get(oldRefreshTokenId); ClientDetailsEntity client = clientRepository.getClientByClientId(clientRef); - Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); + Long newRefreshTokenId = maps.getRefreshTokenOldToNewIdMap().get(oldRefreshTokenId); OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); refreshToken.setClient(client); tokenRepository.saveRefreshToken(refreshToken); } - refreshTokenToClientRefs.clear(); - for (Long oldRefreshTokenId : refreshTokenToAuthHolderRefs.keySet()) { - Long oldAuthHolderId = refreshTokenToAuthHolderRefs.get(oldRefreshTokenId); - Long newAuthHolderId = authHolderOldToNewIdMap.get(oldAuthHolderId); + for (Long oldRefreshTokenId : maps.getRefreshTokenToAuthHolderRefs().keySet()) { + Long oldAuthHolderId = maps.getRefreshTokenToAuthHolderRefs().get(oldRefreshTokenId); + Long newAuthHolderId = maps.getAuthHolderOldToNewIdMap().get(oldAuthHolderId); AuthenticationHolderEntity authHolder = authHolderRepository.getById(newAuthHolderId); - Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); + Long newRefreshTokenId = maps.getRefreshTokenOldToNewIdMap().get(oldRefreshTokenId); OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); refreshToken.setAuthenticationHolder(authHolder); tokenRepository.saveRefreshToken(refreshToken); } - refreshTokenToAuthHolderRefs.clear(); - for (Long oldAccessTokenId : accessTokenToClientRefs.keySet()) { - String clientRef = accessTokenToClientRefs.get(oldAccessTokenId); + for (Long oldAccessTokenId : maps.getAccessTokenToClientRefs().keySet()) { + String clientRef = maps.getAccessTokenToClientRefs().get(oldAccessTokenId); ClientDetailsEntity client = clientRepository.getClientByClientId(clientRef); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + Long newAccessTokenId = maps.getAccessTokenOldToNewIdMap().get(oldAccessTokenId); OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); accessToken.setClient(client); tokenRepository.saveAccessToken(accessToken); } - accessTokenToClientRefs.clear(); - for (Long oldAccessTokenId : accessTokenToAuthHolderRefs.keySet()) { - Long oldAuthHolderId = accessTokenToAuthHolderRefs.get(oldAccessTokenId); - Long newAuthHolderId = authHolderOldToNewIdMap.get(oldAuthHolderId); + maps.getAccessTokenToClientRefs().clear(); + for (Long oldAccessTokenId : maps.getAccessTokenToAuthHolderRefs().keySet()) { + Long oldAuthHolderId = maps.getAccessTokenToAuthHolderRefs().get(oldAccessTokenId); + Long newAuthHolderId = maps.getAuthHolderOldToNewIdMap().get(oldAuthHolderId); AuthenticationHolderEntity authHolder = authHolderRepository.getById(newAuthHolderId); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + Long newAccessTokenId = maps.getAccessTokenOldToNewIdMap().get(oldAccessTokenId); OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); accessToken.setAuthenticationHolder(authHolder); tokenRepository.saveAccessToken(accessToken); } - accessTokenToAuthHolderRefs.clear(); - for (Long oldAccessTokenId : accessTokenToRefreshTokenRefs.keySet()) { - Long oldRefreshTokenId = accessTokenToRefreshTokenRefs.get(oldAccessTokenId); - Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); + for (Long oldAccessTokenId : maps.getAccessTokenToRefreshTokenRefs().keySet()) { + Long oldRefreshTokenId = maps.getAccessTokenToRefreshTokenRefs().get(oldAccessTokenId); + Long newRefreshTokenId = maps.getRefreshTokenOldToNewIdMap().get(oldRefreshTokenId); OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + Long newAccessTokenId = maps.getAccessTokenOldToNewIdMap().get(oldAccessTokenId); OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); accessToken.setRefreshToken(refreshToken); tokenRepository.saveAccessToken(accessToken); } - accessTokenToRefreshTokenRefs.clear(); - refreshTokenOldToNewIdMap.clear(); - for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { - Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); + for (Long oldGrantId : maps.getGrantToAccessTokensRefs().keySet()) { + Set oldAccessTokenIds = maps.getGrantToAccessTokensRefs().get(oldGrantId); - Long newGrantId = grantOldToNewIdMap.get(oldGrantId); + Long newGrantId = maps.getGrantOldToNewIdMap().get(oldGrantId); ApprovedSite site = approvedSiteRepository.getById(newGrantId); for(Long oldTokenId : oldAccessTokenIds) { - Long newTokenId = accessTokenOldToNewIdMap.get(oldTokenId); + Long newTokenId = maps.getAccessTokenOldToNewIdMap().get(oldTokenId); OAuth2AccessTokenEntity token = tokenRepository.getAccessTokenById(newTokenId); token.setApprovedSite(site); tokenRepository.saveAccessToken(token); @@ -931,8 +914,6 @@ private void fixObjectReferences() { approvedSiteRepository.save(site); } - accessTokenOldToNewIdMap.clear(); - grantOldToNewIdMap.clear(); } } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java index 85d3141728..b5f184c3b0 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java @@ -49,6 +49,7 @@ import org.mitre.openid.connect.repository.WhitelistedSiteRepository; import org.mitre.openid.connect.service.MITREidDataService; import org.mitre.openid.connect.service.MITREidDataServiceExtension; +import org.mitre.openid.connect.service.MITREidDataServiceMaps; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -168,6 +169,8 @@ public class MITREidDataService_1_2 extends MITREidDataServiceSupport implements @Autowired(required = false) private List extensions = Collections.emptyList(); + private MITREidDataServiceMaps maps = new MITREidDataServiceMaps(); + private static final String THIS_VERSION = MITREID_CONNECT_1_2; @Override @@ -220,10 +223,8 @@ public void importData(JsonReader reader) throws IOException { } else { for (MITREidDataServiceExtension extension : extensions) { if (extension.supportsVersion(THIS_VERSION)) { - if (extension.canImport(name)) { - extension.importExtensionData(reader); - break; - } + extension.importExtensionData(name, reader); + break; } } // unknown token, skip it @@ -243,15 +244,12 @@ public void importData(JsonReader reader) throws IOException { fixObjectReferences(); for (MITREidDataServiceExtension extension : extensions) { if (extension.supportsVersion(THIS_VERSION)) { - extension.fixExtensionObjectReferences(); + extension.fixExtensionObjectReferences(maps); break; } } + maps.clearAll(); } - private Map refreshTokenToClientRefs = new HashMap(); - private Map refreshTokenToAuthHolderRefs = new HashMap(); - private Map refreshTokenOldToNewIdMap = new HashMap(); - /** * @param reader * @throws IOException @@ -305,19 +303,14 @@ private void readRefreshTokens(JsonReader reader) throws IOException { } reader.endObject(); Long newId = tokenRepository.saveRefreshToken(token).getId(); - refreshTokenToClientRefs.put(currentId, clientId); - refreshTokenToAuthHolderRefs.put(currentId, authHolderId); - refreshTokenOldToNewIdMap.put(currentId, newId); + maps.getRefreshTokenToClientRefs().put(currentId, clientId); + maps.getRefreshTokenToAuthHolderRefs().put(currentId, authHolderId); + maps.getRefreshTokenOldToNewIdMap().put(currentId, newId); logger.debug("Read refresh token {}", currentId); } reader.endArray(); logger.info("Done reading refresh tokens"); } - private Map accessTokenToClientRefs = new HashMap(); - private Map accessTokenToAuthHolderRefs = new HashMap(); - private Map accessTokenToRefreshTokenRefs = new HashMap(); - private Map accessTokenOldToNewIdMap = new HashMap(); - /** * @param reader * @throws IOException @@ -380,19 +373,17 @@ private void readAccessTokens(JsonReader reader) throws IOException { } reader.endObject(); Long newId = tokenRepository.saveAccessToken(token).getId(); - accessTokenToClientRefs.put(currentId, clientId); - accessTokenToAuthHolderRefs.put(currentId, authHolderId); + maps.getAccessTokenToClientRefs().put(currentId, clientId); + maps.getAccessTokenToAuthHolderRefs().put(currentId, authHolderId); if (refreshTokenId != null) { - accessTokenToRefreshTokenRefs.put(currentId, refreshTokenId); + maps.getAccessTokenToRefreshTokenRefs().put(currentId, refreshTokenId); } - accessTokenOldToNewIdMap.put(currentId, newId); + maps.getAccessTokenOldToNewIdMap().put(currentId, newId); logger.debug("Read access token {}", currentId); } reader.endArray(); logger.info("Done reading access tokens"); } - private Map authHolderOldToNewIdMap = new HashMap(); - /** * @param reader * @throws IOException @@ -452,7 +443,7 @@ private void readAuthenticationHolders(JsonReader reader) throws IOException { } reader.endObject(); Long newId = authHolderRepository.save(ahe).getId(); - authHolderOldToNewIdMap.put(currentId, newId); + maps.getAuthHolderOldToNewIdMap().put(currentId, newId); logger.debug("Read authentication holder {}", currentId); } reader.endArray(); @@ -506,9 +497,6 @@ private SavedUserAuthentication readSavedUserAuthentication(JsonReader reader) t return savedUserAuth; } - Map grantOldToNewIdMap = new HashMap<>(); - Map> grantToAccessTokensRefs = new HashMap<>(); - /** * @param reader * @throws IOException @@ -561,17 +549,15 @@ private void readGrants(JsonReader reader) throws IOException { } reader.endObject(); Long newId = approvedSiteRepository.save(site).getId(); - grantOldToNewIdMap.put(currentId, newId); + maps.getGrantOldToNewIdMap().put(currentId, newId); if (tokenIds != null) { - grantToAccessTokensRefs.put(currentId, tokenIds); + maps.getGrantToAccessTokensRefs().put(currentId, tokenIds); } logger.debug("Read grant {}", currentId); } reader.endArray(); logger.info("Done reading grants"); } - Map whitelistedSiteOldToNewIdMap = new HashMap(); - /** * @param reader * @throws IOException @@ -610,7 +596,7 @@ private void readWhitelistedSites(JsonReader reader) throws IOException { } reader.endObject(); Long newId = wlSiteRepository.save(wlSite).getId(); - whitelistedSiteOldToNewIdMap.put(currentId, newId); + maps.getWhitelistedSiteOldToNewIdMap().put(currentId, newId); } reader.endArray(); logger.info("Done reading whitelisted sites"); @@ -857,63 +843,57 @@ private void readSystemScopes(JsonReader reader) throws IOException { private void fixObjectReferences() { logger.info("Fixing object references..."); - for (Long oldRefreshTokenId : refreshTokenToClientRefs.keySet()) { - String clientRef = refreshTokenToClientRefs.get(oldRefreshTokenId); + for (Long oldRefreshTokenId : maps.getRefreshTokenToClientRefs().keySet()) { + String clientRef = maps.getRefreshTokenToClientRefs().get(oldRefreshTokenId); ClientDetailsEntity client = clientRepository.getClientByClientId(clientRef); - Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); + Long newRefreshTokenId = maps.getRefreshTokenOldToNewIdMap().get(oldRefreshTokenId); OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); refreshToken.setClient(client); tokenRepository.saveRefreshToken(refreshToken); } - refreshTokenToClientRefs.clear(); - for (Long oldRefreshTokenId : refreshTokenToAuthHolderRefs.keySet()) { - Long oldAuthHolderId = refreshTokenToAuthHolderRefs.get(oldRefreshTokenId); - Long newAuthHolderId = authHolderOldToNewIdMap.get(oldAuthHolderId); + for (Long oldRefreshTokenId : maps.getRefreshTokenToAuthHolderRefs().keySet()) { + Long oldAuthHolderId = maps.getRefreshTokenToAuthHolderRefs().get(oldRefreshTokenId); + Long newAuthHolderId = maps.getAuthHolderOldToNewIdMap().get(oldAuthHolderId); AuthenticationHolderEntity authHolder = authHolderRepository.getById(newAuthHolderId); - Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); + Long newRefreshTokenId = maps.getRefreshTokenOldToNewIdMap().get(oldRefreshTokenId); OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); refreshToken.setAuthenticationHolder(authHolder); tokenRepository.saveRefreshToken(refreshToken); } - refreshTokenToAuthHolderRefs.clear(); - for (Long oldAccessTokenId : accessTokenToClientRefs.keySet()) { - String clientRef = accessTokenToClientRefs.get(oldAccessTokenId); + for (Long oldAccessTokenId : maps.getAccessTokenToClientRefs().keySet()) { + String clientRef = maps.getAccessTokenToClientRefs().get(oldAccessTokenId); ClientDetailsEntity client = clientRepository.getClientByClientId(clientRef); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + Long newAccessTokenId = maps.getAccessTokenOldToNewIdMap().get(oldAccessTokenId); OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); accessToken.setClient(client); tokenRepository.saveAccessToken(accessToken); } - accessTokenToClientRefs.clear(); - for (Long oldAccessTokenId : accessTokenToAuthHolderRefs.keySet()) { - Long oldAuthHolderId = accessTokenToAuthHolderRefs.get(oldAccessTokenId); - Long newAuthHolderId = authHolderOldToNewIdMap.get(oldAuthHolderId); + for (Long oldAccessTokenId : maps.getAccessTokenToAuthHolderRefs().keySet()) { + Long oldAuthHolderId = maps.getAccessTokenToAuthHolderRefs().get(oldAccessTokenId); + Long newAuthHolderId = maps.getAuthHolderOldToNewIdMap().get(oldAuthHolderId); AuthenticationHolderEntity authHolder = authHolderRepository.getById(newAuthHolderId); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + Long newAccessTokenId = maps.getAccessTokenOldToNewIdMap().get(oldAccessTokenId); OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); accessToken.setAuthenticationHolder(authHolder); tokenRepository.saveAccessToken(accessToken); } - accessTokenToAuthHolderRefs.clear(); - for (Long oldAccessTokenId : accessTokenToRefreshTokenRefs.keySet()) { - Long oldRefreshTokenId = accessTokenToRefreshTokenRefs.get(oldAccessTokenId); - Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); + for (Long oldAccessTokenId : maps.getAccessTokenToRefreshTokenRefs().keySet()) { + Long oldRefreshTokenId = maps.getAccessTokenToRefreshTokenRefs().get(oldAccessTokenId); + Long newRefreshTokenId = maps.getRefreshTokenOldToNewIdMap().get(oldRefreshTokenId); OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + Long newAccessTokenId = maps.getAccessTokenOldToNewIdMap().get(oldAccessTokenId); OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); accessToken.setRefreshToken(refreshToken); tokenRepository.saveAccessToken(accessToken); } - accessTokenToRefreshTokenRefs.clear(); - refreshTokenOldToNewIdMap.clear(); - for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { - Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); + for (Long oldGrantId : maps.getGrantToAccessTokensRefs().keySet()) { + Set oldAccessTokenIds = maps.getGrantToAccessTokensRefs().get(oldGrantId); - Long newGrantId = grantOldToNewIdMap.get(oldGrantId); + Long newGrantId = maps.getGrantOldToNewIdMap().get(oldGrantId); ApprovedSite site = approvedSiteRepository.getById(newGrantId); for(Long oldTokenId : oldAccessTokenIds) { - Long newTokenId = accessTokenOldToNewIdMap.get(oldTokenId); + Long newTokenId = maps.getAccessTokenOldToNewIdMap().get(oldTokenId); OAuth2AccessTokenEntity token = tokenRepository.getAccessTokenById(newTokenId); token.setApprovedSite(site); tokenRepository.saveAccessToken(token); @@ -921,8 +901,6 @@ private void fixObjectReferences() { approvedSiteRepository.save(site); } - accessTokenOldToNewIdMap.clear(); - grantOldToNewIdMap.clear(); logger.info("Done fixing object references."); } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java index ca1a7995fd..c5d8750a06 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java @@ -25,10 +25,8 @@ import java.text.ParseException; import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -54,6 +52,7 @@ import org.mitre.openid.connect.repository.WhitelistedSiteRepository; import org.mitre.openid.connect.service.MITREidDataService; import org.mitre.openid.connect.service.MITREidDataServiceExtension; +import org.mitre.openid.connect.service.MITREidDataServiceMaps; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -72,7 +71,7 @@ /** * - * Data service to import and export MITREid 1.2 configuration. + * Data service to import and export MITREid 1.3 configuration. * * @author jricher * @author arielak @@ -172,6 +171,8 @@ public class MITREidDataService_1_3 extends MITREidDataServiceSupport implements private static final String THIS_VERSION = MITREID_CONNECT_1_3; + private MITREidDataServiceMaps maps = new MITREidDataServiceMaps(); + @Override public boolean supportsVersion(String version) { return THIS_VERSION.equals(version); @@ -236,7 +237,7 @@ public void exportData(JsonWriter writer) throws IOException { } } - writer.endObject(); // end mitreid-connect-1.2 + writer.endObject(); // end mitreid-connect-1.3 } /** @@ -557,7 +558,7 @@ private void writeSystemScopes(JsonWriter writer) { @Override public void importData(JsonReader reader) throws IOException { - logger.info("Reading configuration for 1.2"); + logger.info("Reading configuration for 1.3"); // this *HAS* to start as an object reader.beginObject(); @@ -585,17 +586,21 @@ public void importData(JsonReader reader) throws IOException { } else if (name.equals(SYSTEMSCOPES)) { readSystemScopes(reader); } else { + boolean processed = false; for (MITREidDataServiceExtension extension : extensions) { if (extension.supportsVersion(THIS_VERSION)) { - if (extension.importExtensionData(name, reader)) { + processed = extension.importExtensionData(name, reader); + if (processed) { // if the extension processed data, break out of this inner loop // (only the first extension to claim an extension point gets it) break; } } } - // unknown token, skip it - reader.skipValue(); + if (!processed) { + // unknown token, skip it + reader.skipValue(); + } } break; case END_OBJECT: @@ -611,15 +616,13 @@ public void importData(JsonReader reader) throws IOException { fixObjectReferences(); for (MITREidDataServiceExtension extension : extensions) { if (extension.supportsVersion(THIS_VERSION)) { - extension.fixExtensionObjectReferences(); + extension.fixExtensionObjectReferences(maps); break; } } + maps.clearAll(); } - private Map refreshTokenToClientRefs = new HashMap(); - private Map refreshTokenToAuthHolderRefs = new HashMap(); - private Map refreshTokenOldToNewIdMap = new HashMap(); - + /** * @param reader * @throws IOException @@ -673,19 +676,14 @@ private void readRefreshTokens(JsonReader reader) throws IOException { } reader.endObject(); Long newId = tokenRepository.saveRefreshToken(token).getId(); - refreshTokenToClientRefs.put(currentId, clientId); - refreshTokenToAuthHolderRefs.put(currentId, authHolderId); - refreshTokenOldToNewIdMap.put(currentId, newId); + maps.getRefreshTokenToClientRefs().put(currentId, clientId); + maps.getRefreshTokenToAuthHolderRefs().put(currentId, authHolderId); + maps.getRefreshTokenOldToNewIdMap().put(currentId, newId); logger.debug("Read refresh token {}", currentId); } reader.endArray(); logger.info("Done reading refresh tokens"); } - private Map accessTokenToClientRefs = new HashMap(); - private Map accessTokenToAuthHolderRefs = new HashMap(); - private Map accessTokenToRefreshTokenRefs = new HashMap(); - private Map accessTokenOldToNewIdMap = new HashMap(); - /** * @param reader * @throws IOException @@ -748,19 +746,17 @@ private void readAccessTokens(JsonReader reader) throws IOException { } reader.endObject(); Long newId = tokenRepository.saveAccessToken(token).getId(); - accessTokenToClientRefs.put(currentId, clientId); - accessTokenToAuthHolderRefs.put(currentId, authHolderId); + maps.getAccessTokenToClientRefs().put(currentId, clientId); + maps.getAccessTokenToAuthHolderRefs().put(currentId, authHolderId); if (refreshTokenId != null) { - accessTokenToRefreshTokenRefs.put(currentId, refreshTokenId); + maps.getAccessTokenToRefreshTokenRefs().put(currentId, refreshTokenId); } - accessTokenOldToNewIdMap.put(currentId, newId); + maps.getAccessTokenOldToNewIdMap().put(currentId, newId); logger.debug("Read access token {}", currentId); } reader.endArray(); logger.info("Done reading access tokens"); } - private Map authHolderOldToNewIdMap = new HashMap(); - /** * @param reader * @throws IOException @@ -820,7 +816,7 @@ private void readAuthenticationHolders(JsonReader reader) throws IOException { } reader.endObject(); Long newId = authHolderRepository.save(ahe).getId(); - authHolderOldToNewIdMap.put(currentId, newId); + maps.getAuthHolderOldToNewIdMap().put(currentId, newId); logger.debug("Read authentication holder {}", currentId); } reader.endArray(); @@ -874,9 +870,6 @@ private SavedUserAuthentication readSavedUserAuthentication(JsonReader reader) t return savedUserAuth; } - private Map grantOldToNewIdMap = new HashMap<>(); - private Map> grantToAccessTokensRefs = new HashMap<>(); - /** * @param reader * @throws IOException @@ -929,9 +922,9 @@ private void readGrants(JsonReader reader) throws IOException { } reader.endObject(); Long newId = approvedSiteRepository.save(site).getId(); - grantOldToNewIdMap.put(currentId, newId); + maps.getGrantOldToNewIdMap().put(currentId, newId); if (tokenIds != null) { - grantToAccessTokensRefs.put(currentId, tokenIds); + maps.getGrantToAccessTokensRefs().put(currentId, tokenIds); } logger.debug("Read grant {}", currentId); } @@ -939,8 +932,6 @@ private void readGrants(JsonReader reader) throws IOException { logger.info("Done reading grants"); } - private Map whitelistedSiteOldToNewIdMap = new HashMap(); - /** * @param reader * @throws IOException @@ -979,7 +970,7 @@ private void readWhitelistedSites(JsonReader reader) throws IOException { } reader.endObject(); Long newId = wlSiteRepository.save(wlSite).getId(); - whitelistedSiteOldToNewIdMap.put(currentId, newId); + maps.getWhitelistedSiteOldToNewIdMap().put(currentId, newId); } reader.endArray(); logger.info("Done reading whitelisted sites"); @@ -1230,63 +1221,57 @@ private void readSystemScopes(JsonReader reader) throws IOException { private void fixObjectReferences() { logger.info("Fixing object references..."); - for (Long oldRefreshTokenId : refreshTokenToClientRefs.keySet()) { - String clientRef = refreshTokenToClientRefs.get(oldRefreshTokenId); + for (Long oldRefreshTokenId : maps.getRefreshTokenToClientRefs().keySet()) { + String clientRef = maps.getRefreshTokenToClientRefs().get(oldRefreshTokenId); ClientDetailsEntity client = clientRepository.getClientByClientId(clientRef); - Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); + Long newRefreshTokenId = maps.getRefreshTokenOldToNewIdMap().get(oldRefreshTokenId); OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); refreshToken.setClient(client); tokenRepository.saveRefreshToken(refreshToken); } - refreshTokenToClientRefs.clear(); - for (Long oldRefreshTokenId : refreshTokenToAuthHolderRefs.keySet()) { - Long oldAuthHolderId = refreshTokenToAuthHolderRefs.get(oldRefreshTokenId); - Long newAuthHolderId = authHolderOldToNewIdMap.get(oldAuthHolderId); + for (Long oldRefreshTokenId : maps.getRefreshTokenToAuthHolderRefs().keySet()) { + Long oldAuthHolderId = maps.getRefreshTokenToAuthHolderRefs().get(oldRefreshTokenId); + Long newAuthHolderId = maps.getAuthHolderOldToNewIdMap().get(oldAuthHolderId); AuthenticationHolderEntity authHolder = authHolderRepository.getById(newAuthHolderId); - Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); + Long newRefreshTokenId = maps.getRefreshTokenOldToNewIdMap().get(oldRefreshTokenId); OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); refreshToken.setAuthenticationHolder(authHolder); tokenRepository.saveRefreshToken(refreshToken); } - refreshTokenToAuthHolderRefs.clear(); - for (Long oldAccessTokenId : accessTokenToClientRefs.keySet()) { - String clientRef = accessTokenToClientRefs.get(oldAccessTokenId); + for (Long oldAccessTokenId : maps.getAccessTokenToClientRefs().keySet()) { + String clientRef = maps.getAccessTokenToClientRefs().get(oldAccessTokenId); ClientDetailsEntity client = clientRepository.getClientByClientId(clientRef); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + Long newAccessTokenId = maps.getAccessTokenOldToNewIdMap().get(oldAccessTokenId); OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); accessToken.setClient(client); tokenRepository.saveAccessToken(accessToken); } - accessTokenToClientRefs.clear(); - for (Long oldAccessTokenId : accessTokenToAuthHolderRefs.keySet()) { - Long oldAuthHolderId = accessTokenToAuthHolderRefs.get(oldAccessTokenId); - Long newAuthHolderId = authHolderOldToNewIdMap.get(oldAuthHolderId); + for (Long oldAccessTokenId : maps.getAccessTokenToAuthHolderRefs().keySet()) { + Long oldAuthHolderId = maps.getAccessTokenToAuthHolderRefs().get(oldAccessTokenId); + Long newAuthHolderId = maps.getAuthHolderOldToNewIdMap().get(oldAuthHolderId); AuthenticationHolderEntity authHolder = authHolderRepository.getById(newAuthHolderId); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + Long newAccessTokenId = maps.getAccessTokenOldToNewIdMap().get(oldAccessTokenId); OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); accessToken.setAuthenticationHolder(authHolder); tokenRepository.saveAccessToken(accessToken); } - accessTokenToAuthHolderRefs.clear(); - for (Long oldAccessTokenId : accessTokenToRefreshTokenRefs.keySet()) { - Long oldRefreshTokenId = accessTokenToRefreshTokenRefs.get(oldAccessTokenId); - Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); + for (Long oldAccessTokenId : maps.getAccessTokenToRefreshTokenRefs().keySet()) { + Long oldRefreshTokenId = maps.getAccessTokenToRefreshTokenRefs().get(oldAccessTokenId); + Long newRefreshTokenId = maps.getRefreshTokenOldToNewIdMap().get(oldRefreshTokenId); OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); + Long newAccessTokenId = maps.getAccessTokenOldToNewIdMap().get(oldAccessTokenId); OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); accessToken.setRefreshToken(refreshToken); tokenRepository.saveAccessToken(accessToken); } - accessTokenToRefreshTokenRefs.clear(); - refreshTokenOldToNewIdMap.clear(); - for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { - Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); + for (Long oldGrantId : maps.getGrantToAccessTokensRefs().keySet()) { + Set oldAccessTokenIds = maps.getGrantToAccessTokensRefs().get(oldGrantId); - Long newGrantId = grantOldToNewIdMap.get(oldGrantId); + Long newGrantId = maps.getGrantOldToNewIdMap().get(oldGrantId); ApprovedSite site = approvedSiteRepository.getById(newGrantId); for(Long oldTokenId : oldAccessTokenIds) { - Long newTokenId = accessTokenOldToNewIdMap.get(oldTokenId); + Long newTokenId = maps.getAccessTokenOldToNewIdMap().get(oldTokenId); OAuth2AccessTokenEntity token = tokenRepository.getAccessTokenById(newTokenId); token.setApprovedSite(site); tokenRepository.saveAccessToken(token); @@ -1294,8 +1279,16 @@ private void fixObjectReferences() { approvedSiteRepository.save(site); } + /* + refreshTokenToClientRefs.clear(); + refreshTokenToAuthHolderRefs.clear(); + accessTokenToClientRefs.clear(); + accessTokenToAuthHolderRefs.clear(); + accessTokenToRefreshTokenRefs.clear(); + refreshTokenOldToNewIdMap.clear(); accessTokenOldToNewIdMap.clear(); grantOldToNewIdMap.clear(); + */ logger.info("Done fixing object references."); } diff --git a/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java b/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java deleted file mode 100644 index 61b37058d9..0000000000 --- a/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java +++ /dev/null @@ -1,1815 +0,0 @@ -package org.mitre.openid.connect.service.impl; -/******************************************************************************* - * Copyright 2017 The MITRE Corporation - * and the MIT Internet Trust Consortium - * - * 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. - *******************************************************************************/ - - -import static org.mitre.util.JsonUtils.readMap; -import static org.mitre.util.JsonUtils.readSet; -import static org.mitre.util.JsonUtils.writeNullSafeArray; - -import java.io.IOException; -import java.io.Serializable; -import java.text.ParseException; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.mitre.oauth2.model.AuthenticationHolderEntity; -import org.mitre.oauth2.model.ClientDetailsEntity; -import org.mitre.oauth2.model.ClientDetailsEntity.AppType; -import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; -import org.mitre.oauth2.model.ClientDetailsEntity.SubjectType; -import org.mitre.oauth2.model.OAuth2AccessTokenEntity; -import org.mitre.oauth2.model.OAuth2RefreshTokenEntity; -import org.mitre.oauth2.model.RegisteredClient; -import org.mitre.oauth2.model.SavedUserAuthentication; -import org.mitre.oauth2.model.SystemScope; -import org.mitre.oauth2.repository.AuthenticationHolderRepository; -import org.mitre.oauth2.repository.OAuth2ClientRepository; -import org.mitre.oauth2.repository.OAuth2TokenRepository; -import org.mitre.oauth2.repository.SystemScopeRepository; -import org.mitre.openid.connect.ClientDetailsEntityJsonProcessor; -import org.mitre.openid.connect.model.ApprovedSite; -import org.mitre.openid.connect.model.BlacklistedSite; -import org.mitre.openid.connect.model.WhitelistedSite; -import org.mitre.openid.connect.repository.ApprovedSiteRepository; -import org.mitre.openid.connect.repository.BlacklistedSiteRepository; -import org.mitre.openid.connect.repository.WhitelistedSiteRepository; -import org.mitre.openid.connect.service.MITREidDataService; -import org.mitre.uma.model.Claim; -import org.mitre.uma.model.Permission; -import org.mitre.uma.model.PermissionTicket; -import org.mitre.uma.model.Policy; -import org.mitre.uma.model.ResourceSet; -import org.mitre.uma.model.SavedRegisteredClient; -import org.mitre.uma.repository.PermissionRepository; -import org.mitre.uma.repository.ResourceSetRepository; -import org.mitre.uma.service.impl.JpaRegisteredClientService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.stereotype.Service; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import com.nimbusds.jose.EncryptionMethod; -import com.nimbusds.jose.JWEAlgorithm; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.jwk.JWKSet; -import com.nimbusds.jwt.JWTParser; - -/** - * - * UMA EXPORT OVERRIDE - * - * Data service to import and export MITREid 1.2 configuration. - * - * @author jricher - * @author arielak - */ -@Service -@SuppressWarnings(value = {"unchecked"}) -public class MITREidDataService_1_2 extends MITREidDataServiceSupport implements MITREidDataService { - - private static final String REGISTERED_CLIENT = "registeredClient"; - private static final String DEFAULT_SCOPE = "defaultScope"; - private static final String STRUCTURED_PARAMETER = "structuredParameter"; - private static final String STRUCTURED = "structured"; - private static final String RESTRICTED = "restricted"; - private static final String ICON = "icon"; - private static final String DYNAMICALLY_REGISTERED = "dynamicallyRegistered"; - private static final String CLEAR_ACCESS_TOKENS_ON_REFRESH = "clearAccessTokensOnRefresh"; - private static final String REUSE_REFRESH_TOKEN = "reuseRefreshToken"; - private static final String ALLOW_INTROSPECTION = "allowIntrospection"; - private static final String DESCRIPTION = "description"; - private static final String REQUEST_URIS = "requestUris"; - private static final String POST_LOGOUT_REDIRECT_URI = "postLogoutRedirectUri"; - private static final String INTITATE_LOGIN_URI = "intitateLoginUri"; - private static final String DEFAULT_ACR_VALUES = "defaultACRValues"; - private static final String REQUIRE_AUTH_TIME = "requireAuthTime"; - private static final String DEFAULT_MAX_AGE = "defaultMaxAge"; - private static final String TOKEN_ENDPOINT_AUTH_SIGNING_ALG = "tokenEndpointAuthSigningAlg"; - private static final String USER_INFO_ENCRYPTED_RESPONSE_ENC = "userInfoEncryptedResponseEnc"; - private static final String USER_INFO_ENCRYPTED_RESPONSE_ALG = "userInfoEncryptedResponseAlg"; - private static final String USER_INFO_SIGNED_RESPONSE_ALG = "userInfoSignedResponseAlg"; - private static final String ID_TOKEN_ENCRYPTED_RESPONSE_ENC = "idTokenEncryptedResponseEnc"; - private static final String ID_TOKEN_ENCRYPTED_RESPONSE_ALG = "idTokenEncryptedResponseAlg"; - private static final String ID_TOKEN_SIGNED_RESPONSE_ALG = "idTokenSignedResponseAlg"; - private static final String REQUEST_OBJECT_SIGNING_ALG = "requestObjectSigningAlg"; - private static final String SUBJECT_TYPE = "subjectType"; - private static final String SECTOR_IDENTIFIER_URI = "sectorIdentifierUri"; - private static final String APPLICATION_TYPE = "applicationType"; - private static final String JWKS = "jwks"; - private static final String JWKS_URI = "jwksUri"; - private static final String POLICY_URI = "policyUri"; - private static final String GRANT_TYPES = "grantTypes"; - private static final String TOKEN_ENDPOINT_AUTH_METHOD = "tokenEndpointAuthMethod"; - private static final String TOS_URI = "tosUri"; - private static final String CONTACTS = "contacts"; - private static final String LOGO_URI = "logoUri"; - private static final String REDIRECT_URIS = "redirectUris"; - private static final String REFRESH_TOKEN_VALIDITY_SECONDS = "refreshTokenValiditySeconds"; - private static final String ACCESS_TOKEN_VALIDITY_SECONDS = "accessTokenValiditySeconds"; - private static final String SECRET = "secret"; - private static final String URI = "uri"; - private static final String CREATOR_USER_ID = "creatorUserId"; - private static final String APPROVED_ACCESS_TOKENS = "approvedAccessTokens"; - private static final String ALLOWED_SCOPES = "allowedScopes"; - private static final String USER_ID = "userId"; - private static final String TIMEOUT_DATE = "timeoutDate"; - private static final String CREATION_DATE = "creationDate"; - private static final String ACCESS_DATE = "accessDate"; - private static final String AUTHENTICATED = "authenticated"; - private static final String SOURCE_CLASS = "sourceClass"; - private static final String NAME = "name"; - private static final String SAVED_USER_AUTHENTICATION = "savedUserAuthentication"; - private static final String EXTENSIONS = "extensions"; - private static final String RESPONSE_TYPES = "responseTypes"; - private static final String REDIRECT_URI = "redirectUri"; - private static final String APPROVED = "approved"; - private static final String AUTHORITIES = "authorities"; - private static final String RESOURCE_IDS = "resourceIds"; - private static final String REQUEST_PARAMETERS = "requestParameters"; - private static final String TYPE = "type"; - private static final String SCOPE = "scope"; - private static final String ID_TOKEN_ID = "idTokenId"; - private static final String REFRESH_TOKEN_ID = "refreshTokenId"; - private static final String VALUE = "value"; - private static final String AUTHENTICATION_HOLDER_ID = "authenticationHolderId"; - private static final String CLIENT_ID = "clientId"; - private static final String EXPIRATION = "expiration"; - private static final String ID = "id"; - private static final String ICON_URI = "iconUri"; - private static final String OWNER = "owner"; - private static final String POLICIES = "policies"; - private static final String SCOPES = "scopes"; - private static final String CLAIMS_REQUIRED = "claimsRequired"; - private static final String ISSUER = "issuer"; - private static final String CLAIM_TOKEN_FORMAT = "claimTokenFormat"; - private static final String CLAIM_TYPE = "claimType"; - private static final String FRIENDLY_NAME = "friendlyName"; - private static final String PERMISSIONS = "permissions"; - private static final String RESOURCE_SET = "resourceSet"; - private static final String PERMISSION_TICKETS = "permissionTickets"; - private static final String PERMISSION = "permission"; - private static final String TICKET = "ticket"; - private static final String CLAIMS_SUPPLIED = "claimsSupplied"; - - private static final String SAVED_REGISTERED_CLIENTS = "savedRegisteredClients"; - private static final String RESOURCE_SETS = "resourceSets"; - - /** - * Logger for this class - */ - private static final Logger logger = LoggerFactory.getLogger(MITREidDataService_1_2.class); - @Autowired - private OAuth2ClientRepository clientRepository; - @Autowired - private ApprovedSiteRepository approvedSiteRepository; - @Autowired - private WhitelistedSiteRepository wlSiteRepository; - @Autowired - private BlacklistedSiteRepository blSiteRepository; - @Autowired - private AuthenticationHolderRepository authHolderRepository; - @Autowired - private OAuth2TokenRepository tokenRepository; - @Autowired - private SystemScopeRepository sysScopeRepository; - @Autowired - private JpaRegisteredClientService registeredClientService; - @Autowired - private ResourceSetRepository resourceSetRepository; - @Autowired - private PermissionRepository permissionRepository; - - /* (non-Javadoc) - * @see org.mitre.openid.connect.service.MITREidDataService#export(com.google.gson.stream.JsonWriter) - */ - @Override - public void exportData(JsonWriter writer) throws IOException { - - // version tag at the root - writer.name(MITREID_CONNECT_1_2); - - writer.beginObject(); - - // clients list - writer.name(CLIENTS); - writer.beginArray(); - writeClients(writer); - writer.endArray(); - - writer.name(GRANTS); - writer.beginArray(); - writeGrants(writer); - writer.endArray(); - - writer.name(WHITELISTEDSITES); - writer.beginArray(); - writeWhitelistedSites(writer); - writer.endArray(); - - writer.name(BLACKLISTEDSITES); - writer.beginArray(); - writeBlacklistedSites(writer); - writer.endArray(); - - writer.name(AUTHENTICATIONHOLDERS); - writer.beginArray(); - writeAuthenticationHolders(writer); - writer.endArray(); - - writer.name(ACCESSTOKENS); - writer.beginArray(); - writeAccessTokens(writer); - writer.endArray(); - - writer.name(REFRESHTOKENS); - writer.beginArray(); - writeRefreshTokens(writer); - writer.endArray(); - - writer.name(SYSTEMSCOPES); - writer.beginArray(); - writeSystemScopes(writer); - writer.endArray(); - - writer.name(SAVED_REGISTERED_CLIENTS); - writer.beginArray(); - writeSavedRegisteredClients(writer); - writer.endArray(); - - writer.name(RESOURCE_SETS); - writer.beginArray(); - writeResourceSets(writer); - writer.endArray(); - - writer.name(PERMISSION_TICKETS); - writer.beginArray(); - writePermissionTickets(writer); - writer.endArray(); - - writer.endObject(); // end mitreid-connect-1.2 - } - - /** - * @param writer - * @throws IOException - */ - private void writePermissionTickets(JsonWriter writer) throws IOException { - for (PermissionTicket ticket : permissionRepository.getAll()) { - writer.beginObject(); - - writer.name(CLAIMS_SUPPLIED); - writer.beginArray(); - for (Claim claim : ticket.getClaimsSupplied()) { - writer.beginObject(); - - writer.name(ISSUER); - writer.beginArray(); - for (String issuer : claim.getIssuer()) { - writer.value(issuer); - } - writer.endArray(); - writer.name(CLAIM_TOKEN_FORMAT); - writer.beginArray(); - for (String format : claim.getClaimTokenFormat()) { - writer.value(format); - } - writer.endArray(); - writer.name(CLAIM_TYPE).value(claim.getClaimType()); - writer.name(FRIENDLY_NAME).value(claim.getFriendlyName()); - writer.name(NAME).value(claim.getName()); - writer.name(VALUE).value(claim.getValue().toString()); - writer.endObject(); - } - writer.endArray(); - - writer.name(EXPIRATION).value(toUTCString(ticket.getExpiration())); - - writer.name(PERMISSION); - writer.beginObject(); - Permission p = ticket.getPermission(); - writer.name(RESOURCE_SET).value(p.getResourceSet().getId()); - writer.name(SCOPES); - writer.beginArray(); - for (String s : p.getScopes()) { - writer.value(s); - } - writer.endArray(); - writer.endObject(); - - writer.name(TICKET).value(ticket.getTicket()); - - writer.endObject(); - } - - - } - - /** - * @param writer - * @throws IOException - */ - private void writeResourceSets(JsonWriter writer) throws IOException { - for (ResourceSet rs : resourceSetRepository.getAll()) { - writer.beginObject(); - writer.name(ID).value(rs.getId()); - writer.name(CLIENT_ID).value(rs.getClientId()); - writer.name(ICON_URI).value(rs.getIconUri()); - writer.name(NAME).value(rs.getName()); - writer.name(TYPE).value(rs.getType()); - writer.name(URI).value(rs.getUri()); - writer.name(OWNER).value(rs.getOwner()); - writer.name(POLICIES); - writer.beginArray(); - for (Policy policy : rs.getPolicies()) { - writer.beginObject(); - writer.name(NAME).value(policy.getName()); - writer.name(SCOPES); - writer.beginArray(); - for (String scope : policy.getScopes()) { - writer.value(scope); - } - writer.endArray(); - writer.name(CLAIMS_REQUIRED); - writer.beginArray(); - for (Claim claim : policy.getClaimsRequired()) { - writer.beginObject(); - - writer.name(ISSUER); - writer.beginArray(); - for (String issuer : claim.getIssuer()) { - writer.value(issuer); - } - writer.endArray(); - writer.name(CLAIM_TOKEN_FORMAT); - writer.beginArray(); - for (String format : claim.getClaimTokenFormat()) { - writer.value(format); - } - writer.endArray(); - writer.name(CLAIM_TYPE).value(claim.getClaimType()); - writer.name(FRIENDLY_NAME).value(claim.getFriendlyName()); - writer.name(NAME).value(claim.getName()); - writer.name(VALUE).value(claim.getValue().toString()); - writer.endObject(); - } - writer.endArray(); - writer.endObject(); - } - writer.endArray(); - writer.name(SCOPES); - writer.beginArray(); - for (String scope : rs.getScopes()) { - writer.value(scope); - } - writer.endArray(); - writer.endObject(); - logger.debug("Finished writing resource set {}", rs.getId()); - } - - } - - /** - * @param writer - */ - private void writeSavedRegisteredClients(JsonWriter writer) throws IOException { - for (SavedRegisteredClient src : registeredClientService.getAll()) { - writer.beginObject(); - writer.name(ISSUER).value(src.getIssuer()); - writer.name(REGISTERED_CLIENT).value(src.getRegisteredClient().getSource().toString()); - writer.endObject(); - logger.debug("Wrote saved registered client {}", src.getId()); - } - logger.info("Done writing saved registered clients"); - } - - /** - * @param writer - */ - private void writeRefreshTokens(JsonWriter writer) throws IOException { - for (OAuth2RefreshTokenEntity token : tokenRepository.getAllRefreshTokens()) { - writer.beginObject(); - writer.name(ID).value(token.getId()); - writer.name(EXPIRATION).value(toUTCString(token.getExpiration())); - writer.name(CLIENT_ID) - .value((token.getClient() != null) ? token.getClient().getClientId() : null); - writer.name(AUTHENTICATION_HOLDER_ID) - .value((token.getAuthenticationHolder() != null) ? token.getAuthenticationHolder().getId() : null); - writer.name(VALUE).value(token.getValue()); - writer.endObject(); - logger.debug("Wrote refresh token {}", token.getId()); - } - logger.info("Done writing refresh tokens"); - } - - /** - * @param writer - */ - private void writeAccessTokens(JsonWriter writer) throws IOException { - for (OAuth2AccessTokenEntity token : tokenRepository.getAllAccessTokens()) { - writer.beginObject(); - writer.name(ID).value(token.getId()); - writer.name(EXPIRATION).value(toUTCString(token.getExpiration())); - writer.name(CLIENT_ID) - .value((token.getClient() != null) ? token.getClient().getClientId() : null); - writer.name(AUTHENTICATION_HOLDER_ID) - .value((token.getAuthenticationHolder() != null) ? token.getAuthenticationHolder().getId() : null); - writer.name(REFRESH_TOKEN_ID) - .value((token.getRefreshToken() != null) ? token.getRefreshToken().getId() : null); -/* - writer.name(ID_TOKEN_ID) - .value((token.getIdToken() != null) ? token.getIdToken().getId() : null); -*/ - writer.name(SCOPE); - writer.beginArray(); - for (String s : token.getScope()) { - writer.value(s); - } - writer.endArray(); - writer.name(PERMISSIONS); - writer.beginArray(); - for (Permission p : token.getPermissions()) { - writer.beginObject(); - writer.name(RESOURCE_SET).value(p.getResourceSet().getId()); - writer.name(SCOPES); - writer.beginArray(); - for (String s : p.getScopes()) { - writer.value(s); - } - writer.endArray(); - writer.endObject(); - } - writer.endArray(); - - writer.name(TYPE).value(token.getTokenType()); - writer.name(VALUE).value(token.getValue()); - writer.endObject(); - logger.debug("Wrote access token {}", token.getId()); - } - logger.info("Done writing access tokens"); - } - - /** - * @param writer - */ - private void writeAuthenticationHolders(JsonWriter writer) throws IOException { - for (AuthenticationHolderEntity holder : authHolderRepository.getAll()) { - writer.beginObject(); - writer.name(ID).value(holder.getId()); - - writer.name(REQUEST_PARAMETERS); - writer.beginObject(); - for (Entry entry : holder.getRequestParameters().entrySet()) { - writer.name(entry.getKey()).value(entry.getValue()); - } - writer.endObject(); - writer.name(CLIENT_ID).value(holder.getClientId()); - Set scope = holder.getScope(); - writer.name(SCOPE); - writer.beginArray(); - for (String s : scope) { - writer.value(s); - } - writer.endArray(); - writer.name(RESOURCE_IDS); - writer.beginArray(); - if (holder.getResourceIds() != null) { - for (String s : holder.getResourceIds()) { - writer.value(s); - } - } - writer.endArray(); - writer.name(AUTHORITIES); - writer.beginArray(); - for (GrantedAuthority authority : holder.getAuthorities()) { - writer.value(authority.getAuthority()); - } - writer.endArray(); - writer.name(APPROVED).value(holder.isApproved()); - writer.name(REDIRECT_URI).value(holder.getRedirectUri()); - writer.name(RESPONSE_TYPES); - writer.beginArray(); - for (String s : holder.getResponseTypes()) { - writer.value(s); - } - writer.endArray(); - writer.name(EXTENSIONS); - writer.beginObject(); - for (Entry entry : holder.getExtensions().entrySet()) { - // while the extension map itself is Serializable, we enforce storage of Strings - if (entry.getValue() instanceof String) { - writer.name(entry.getKey()).value((String) entry.getValue()); - } else { - logger.warn("Skipping non-string extension: " + entry); - } - } - writer.endObject(); - - writer.name(SAVED_USER_AUTHENTICATION); - if (holder.getUserAuth() != null) { - writer.beginObject(); - writer.name(NAME).value(holder.getUserAuth().getName()); - writer.name(SOURCE_CLASS).value(holder.getUserAuth().getSourceClass()); - writer.name(AUTHENTICATED).value(holder.getUserAuth().isAuthenticated()); - writer.name(AUTHORITIES); - writer.beginArray(); - for (GrantedAuthority authority : holder.getUserAuth().getAuthorities()) { - writer.value(authority.getAuthority()); - } - writer.endArray(); - - writer.endObject(); - } else { - writer.nullValue(); - } - - - writer.endObject(); - logger.debug("Wrote authentication holder {}", holder.getId()); - } - logger.info("Done writing authentication holders"); - } - - /** - * @param writer - */ - private void writeGrants(JsonWriter writer) throws IOException { - for (ApprovedSite site : approvedSiteRepository.getAll()) { - writer.beginObject(); - writer.name(ID).value(site.getId()); - writer.name(ACCESS_DATE).value(toUTCString(site.getAccessDate())); - writer.name(CLIENT_ID).value(site.getClientId()); - writer.name(CREATION_DATE).value(toUTCString(site.getCreationDate())); - writer.name(TIMEOUT_DATE).value(toUTCString(site.getTimeoutDate())); - writer.name(USER_ID).value(site.getUserId()); - writer.name(ALLOWED_SCOPES); - writeNullSafeArray(writer, site.getAllowedScopes()); - List tokens = tokenRepository.getAccessTokensForApprovedSite(site); - writer.name(APPROVED_ACCESS_TOKENS); - writer.beginArray(); - for (OAuth2AccessTokenEntity token : tokens) { - writer.value(token.getId()); - } - writer.endArray(); - writer.endObject(); - logger.debug("Wrote grant {}", site.getId()); - } - logger.info("Done writing grants"); - } - - /** - * @param writer - */ - private void writeWhitelistedSites(JsonWriter writer) throws IOException { - for (WhitelistedSite wlSite : wlSiteRepository.getAll()) { - writer.beginObject(); - writer.name(ID).value(wlSite.getId()); - writer.name(CLIENT_ID).value(wlSite.getClientId()); - writer.name(CREATOR_USER_ID).value(wlSite.getCreatorUserId()); - writer.name(ALLOWED_SCOPES); - writeNullSafeArray(writer, wlSite.getAllowedScopes()); - writer.endObject(); - logger.debug("Wrote whitelisted site {}", wlSite.getId()); - } - logger.info("Done writing whitelisted sites"); - } - - /** - * @param writer - */ - private void writeBlacklistedSites(JsonWriter writer) throws IOException { - for (BlacklistedSite blSite : blSiteRepository.getAll()) { - writer.beginObject(); - writer.name(ID).value(blSite.getId()); - writer.name(URI).value(blSite.getUri()); - writer.endObject(); - logger.debug("Wrote blacklisted site {}", blSite.getId()); - } - logger.info("Done writing blacklisted sites"); - } - - /** - * @param writer - */ - private void writeClients(JsonWriter writer) { - for (ClientDetailsEntity client : clientRepository.getAllClients()) { - try { - writer.beginObject(); - writer.name(CLIENT_ID).value(client.getClientId()); - writer.name(RESOURCE_IDS); - writeNullSafeArray(writer, client.getResourceIds()); - - writer.name(SECRET).value(client.getClientSecret()); - - writer.name(SCOPE); - writeNullSafeArray(writer, client.getScope()); - - writer.name(AUTHORITIES); - writer.beginArray(); - for (GrantedAuthority authority : client.getAuthorities()) { - writer.value(authority.getAuthority()); - } - writer.endArray(); - writer.name(ACCESS_TOKEN_VALIDITY_SECONDS).value(client.getAccessTokenValiditySeconds()); - writer.name(REFRESH_TOKEN_VALIDITY_SECONDS).value(client.getRefreshTokenValiditySeconds()); - writer.name(REDIRECT_URIS); - writeNullSafeArray(writer, client.getRedirectUris()); - writer.name(NAME).value(client.getClientName()); - writer.name(URI).value(client.getClientUri()); - writer.name(LOGO_URI).value(client.getLogoUri()); - writer.name(CONTACTS); - writeNullSafeArray(writer, client.getContacts()); - writer.name(TOS_URI).value(client.getTosUri()); - writer.name(TOKEN_ENDPOINT_AUTH_METHOD) - .value((client.getTokenEndpointAuthMethod() != null) ? client.getTokenEndpointAuthMethod().getValue() : null); - writer.name(GRANT_TYPES); - writer.beginArray(); - for (String s : client.getGrantTypes()) { - writer.value(s); - } - writer.endArray(); - writer.name(RESPONSE_TYPES); - writer.beginArray(); - for (String s : client.getResponseTypes()) { - writer.value(s); - } - writer.endArray(); - writer.name(POLICY_URI).value(client.getPolicyUri()); - writer.name(JWKS_URI).value(client.getJwksUri()); - writer.name(JWKS).value((client.getJwks() != null) ? client.getJwks().toString() : null); - writer.name(APPLICATION_TYPE) - .value((client.getApplicationType() != null) ? client.getApplicationType().getValue() : null); - writer.name(SECTOR_IDENTIFIER_URI).value(client.getSectorIdentifierUri()); - writer.name(SUBJECT_TYPE) - .value((client.getSubjectType() != null) ? client.getSubjectType().getValue() : null); - writer.name(REQUEST_OBJECT_SIGNING_ALG) - .value((client.getRequestObjectSigningAlg() != null) ? client.getRequestObjectSigningAlg().getName() : null); - writer.name(ID_TOKEN_SIGNED_RESPONSE_ALG) - .value((client.getIdTokenSignedResponseAlg() != null) ? client.getIdTokenSignedResponseAlg().getName() : null); - writer.name(ID_TOKEN_ENCRYPTED_RESPONSE_ALG) - .value((client.getIdTokenEncryptedResponseAlg() != null) ? client.getIdTokenEncryptedResponseAlg().getName() : null); - writer.name(ID_TOKEN_ENCRYPTED_RESPONSE_ENC) - .value((client.getIdTokenEncryptedResponseEnc() != null) ? client.getIdTokenEncryptedResponseEnc().getName() : null); - writer.name(USER_INFO_SIGNED_RESPONSE_ALG) - .value((client.getUserInfoSignedResponseAlg() != null) ? client.getUserInfoSignedResponseAlg().getName() : null); - writer.name(USER_INFO_ENCRYPTED_RESPONSE_ALG) - .value((client.getUserInfoEncryptedResponseAlg() != null) ? client.getUserInfoEncryptedResponseAlg().getName() : null); - writer.name(USER_INFO_ENCRYPTED_RESPONSE_ENC) - .value((client.getUserInfoEncryptedResponseEnc() != null) ? client.getUserInfoEncryptedResponseEnc().getName() : null); - writer.name(TOKEN_ENDPOINT_AUTH_SIGNING_ALG) - .value((client.getTokenEndpointAuthSigningAlg() != null) ? client.getTokenEndpointAuthSigningAlg().getName() : null); - writer.name(DEFAULT_MAX_AGE).value(client.getDefaultMaxAge()); - Boolean requireAuthTime = null; - try { - requireAuthTime = client.getRequireAuthTime(); - } catch (NullPointerException e) { - } - if (requireAuthTime != null) { - writer.name(REQUIRE_AUTH_TIME).value(requireAuthTime); - } - writer.name(DEFAULT_ACR_VALUES); - writeNullSafeArray(writer, client.getDefaultACRvalues()); - writer.name(INTITATE_LOGIN_URI).value(client.getInitiateLoginUri()); - writer.name(POST_LOGOUT_REDIRECT_URI); - writeNullSafeArray(writer, client.getPostLogoutRedirectUris()); - writer.name(REQUEST_URIS); - writeNullSafeArray(writer, client.getRequestUris()); - writer.name(DESCRIPTION).value(client.getClientDescription()); - writer.name(ALLOW_INTROSPECTION).value(client.isAllowIntrospection()); - writer.name(REUSE_REFRESH_TOKEN).value(client.isReuseRefreshToken()); - writer.name(CLEAR_ACCESS_TOKENS_ON_REFRESH).value(client.isClearAccessTokensOnRefresh()); - writer.name(DYNAMICALLY_REGISTERED).value(client.isDynamicallyRegistered()); - writer.endObject(); - logger.debug("Wrote client {}", client.getId()); - } catch (IOException ex) { - logger.error("Unable to write client {}", client.getId(), ex); - } - } - logger.info("Done writing clients"); - } - - /** - * @param writer - */ - private void writeSystemScopes(JsonWriter writer) { - for (SystemScope sysScope : sysScopeRepository.getAll()) { - try { - writer.beginObject(); - writer.name(ID).value(sysScope.getId()); - writer.name(DESCRIPTION).value(sysScope.getDescription()); - writer.name(ICON).value(sysScope.getIcon()); - writer.name(VALUE).value(sysScope.getValue()); - writer.name(RESTRICTED).value(sysScope.isRestricted()); - writer.name(DEFAULT_SCOPE).value(sysScope.isDefaultScope()); - writer.endObject(); - logger.debug("Wrote system scope {}", sysScope.getId()); - } catch (IOException ex) { - logger.error("Unable to write system scope {}", sysScope.getId(), ex); - } - } - logger.info("Done writing system scopes"); - } - - /* (non-Javadoc) - * @see org.mitre.openid.connect.service.MITREidDataService#importData(com.google.gson.stream.JsonReader) - */ - @Override - public void importData(JsonReader reader) throws IOException { - - logger.info("Reading configuration for 1.2"); - - // this *HAS* to start as an object - reader.beginObject(); - - while (reader.hasNext()) { - JsonToken tok = reader.peek(); - switch (tok) { - case NAME: - String name = reader.nextName(); - // find out which member it is - if (name.equals(CLIENTS)) { - readClients(reader); - } else if (name.equals(GRANTS)) { - readGrants(reader); - } else if (name.equals(WHITELISTEDSITES)) { - readWhitelistedSites(reader); - } else if (name.equals(BLACKLISTEDSITES)) { - readBlacklistedSites(reader); - } else if (name.equals(AUTHENTICATIONHOLDERS)) { - readAuthenticationHolders(reader); - } else if (name.equals(ACCESSTOKENS)) { - readAccessTokens(reader); - } else if (name.equals(REFRESHTOKENS)) { - readRefreshTokens(reader); - } else if (name.equals(SYSTEMSCOPES)) { - readSystemScopes(reader); - } else if (name.equals(SAVED_REGISTERED_CLIENTS)) { - readSavedRegisteredClients(reader); - } else if (name.equals(RESOURCE_SETS)) { - readResourceSets(reader); - } else if (name.equals(PERMISSION_TICKETS)) { - readPermissionTickets(reader); - } else { - // unknown token, skip it - reader.skipValue(); - } - break; - case END_OBJECT: - // the object ended, we're done here - reader.endObject(); - continue; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - fixObjectReferences(); - } - - - /** - * @param reader - */ - private void readPermissionTickets(JsonReader reader) throws IOException { - JsonParser parser = new JsonParser(); - reader.beginArray(); - while (reader.hasNext()) { - PermissionTicket ticket = new PermissionTicket(); - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(CLAIMS_SUPPLIED)) { - Set claimsSupplied = new HashSet<>(); - reader.beginArray(); - while (reader.hasNext()) { - Claim c = new Claim(); - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String cname = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (cname.equals(ISSUER)) { - c.setIssuer(readSet(reader)); - } else if (cname.equals(CLAIM_TOKEN_FORMAT)) { - c.setClaimTokenFormat(readSet(reader)); - } else if (cname.equals(CLAIM_TYPE)) { - c.setClaimType(reader.nextString()); - } else if (cname.equals(FRIENDLY_NAME)) { - c.setFriendlyName(reader.nextString()); - } else if (cname.equals(NAME)) { - c.setName(reader.nextString()); - } else if (cname.equals(VALUE)) { - JsonElement e = parser.parse(reader.nextString()); - c.setValue(e); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - claimsSupplied.add(c); - } - reader.endArray(); - ticket.setClaimsSupplied(claimsSupplied); - } else if (name.equals(EXPIRATION)) { - ticket.setExpiration(utcToDate(reader.nextString())); - } else if (name.equals(PERMISSION)) { - Permission p = new Permission(); - Long rsid = null; - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String pname = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (pname.equals(RESOURCE_SET)) { - rsid = reader.nextLong(); - } else if (pname.equals(SCOPES)) { - p.setScopes(readSet(reader)); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - Permission saved = permissionRepository.saveRawPermission(p); - permissionToResourceRefs.put(saved.getId(), rsid); - ticket.setPermission(saved); - } else if (name.equals(TICKET)) { - ticket.setTicket(reader.nextString()); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - permissionRepository.save(ticket); - } - reader.endArray(); - } - - - private Map resourceSetOldToNewIdMap = new HashMap<>(); - - /** - * @param reader - */ - private void readResourceSets(JsonReader reader) throws IOException { - JsonParser parser = new JsonParser(); - reader.beginArray(); - while (reader.hasNext()) { - Long oldId = null; - ResourceSet rs = new ResourceSet(); - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ID)) { - oldId = reader.nextLong(); - } else if (name.equals(CLIENT_ID)) { - rs.setClientId(reader.nextString()); - } else if (name.equals(ICON_URI)) { - rs.setIconUri(reader.nextString()); - } else if (name.equals(NAME)) { - rs.setName(reader.nextString()); - } else if (name.equals(TYPE)) { - rs.setType(reader.nextString()); - } else if (name.equals(URI)) { - rs.setUri(reader.nextString()); - } else if (name.equals(OWNER)) { - rs.setOwner(reader.nextString()); - } else if (name.equals(POLICIES)) { - Set policies = new HashSet<>(); - reader.beginArray(); - while (reader.hasNext()) { - Policy p = new Policy(); - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String pname = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (pname.equals(NAME)) { - p.setName(reader.nextString()); - } else if (pname.equals(SCOPES)) { - p.setScopes(readSet(reader)); - } else if (pname.equals(CLAIMS_REQUIRED)) { - Set claimsRequired = new HashSet<>(); - reader.beginArray(); - while (reader.hasNext()) { - Claim c = new Claim(); - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String cname = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (cname.equals(ISSUER)) { - c.setIssuer(readSet(reader)); - } else if (cname.equals(CLAIM_TOKEN_FORMAT)) { - c.setClaimTokenFormat(readSet(reader)); - } else if (cname.equals(CLAIM_TYPE)) { - c.setClaimType(reader.nextString()); - } else if (cname.equals(FRIENDLY_NAME)) { - c.setFriendlyName(reader.nextString()); - } else if (cname.equals(NAME)) { - c.setName(reader.nextString()); - } else if (cname.equals(VALUE)) { - JsonElement e = parser.parse(reader.nextString()); - c.setValue(e); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - claimsRequired.add(c); - } - reader.endArray(); - p.setClaimsRequired(claimsRequired); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - policies.add(p); - } - reader.endArray(); - rs.setPolicies(policies); - } else if (name.equals(SCOPES)) { - rs.setScopes(readSet(reader)); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - Long newId = resourceSetRepository.save(rs).getId(); - resourceSetOldToNewIdMap.put(oldId, newId); - } - reader.endArray(); - logger.info("Done reading resource sets"); - } - - /** - * @param reader - */ - private void readSavedRegisteredClients(JsonReader reader) throws IOException{ - reader.beginArray(); - while (reader.hasNext()) { - String issuer = null; - String clientString = null; - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ISSUER)) { - issuer = reader.nextString(); - } else if (name.equals(REGISTERED_CLIENT)) { - clientString = reader.nextString(); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - RegisteredClient client = ClientDetailsEntityJsonProcessor.parseRegistered(clientString); - registeredClientService.save(issuer, client); - logger.debug("Saved registered client"); - } - reader.endArray(); - logger.info("Done reading saved registered clients"); - } - - private Map refreshTokenToClientRefs = new HashMap(); - private Map refreshTokenToAuthHolderRefs = new HashMap(); - private Map refreshTokenOldToNewIdMap = new HashMap(); - /** - * @param reader - * @throws IOException - */ - private void readRefreshTokens(JsonReader reader) throws IOException { - reader.beginArray(); - while (reader.hasNext()) { - OAuth2RefreshTokenEntity token = new OAuth2RefreshTokenEntity(); - reader.beginObject(); - Long currentId = null; - String clientId = null; - Long authHolderId = null; - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ID)) { - currentId = reader.nextLong(); - } else if (name.equals(EXPIRATION)) { - Date date = utcToDate(reader.nextString()); - token.setExpiration(date); - } else if (name.equals(VALUE)) { - String value = reader.nextString(); - try { - token.setJwt(JWTParser.parse(value)); - } catch (ParseException ex) { - logger.error("Unable to set refresh token value to {}", value, ex); - } - } else if (name.equals(CLIENT_ID)) { - clientId = reader.nextString(); - } else if (name.equals(AUTHENTICATION_HOLDER_ID)) { - authHolderId = reader.nextLong(); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - Long newId = tokenRepository.saveRefreshToken(token).getId(); - refreshTokenToClientRefs.put(currentId, clientId); - refreshTokenToAuthHolderRefs.put(currentId, authHolderId); - refreshTokenOldToNewIdMap.put(currentId, newId); - logger.debug("Read refresh token {}", currentId); - } - reader.endArray(); - logger.info("Done reading refresh tokens"); - } - private Map accessTokenToClientRefs = new HashMap(); - private Map accessTokenToAuthHolderRefs = new HashMap(); - private Map accessTokenToRefreshTokenRefs = new HashMap(); - private Map accessTokenToIdTokenRefs = new HashMap(); - private Map accessTokenOldToNewIdMap = new HashMap(); - private Map permissionToResourceRefs = new HashMap<>(); - - /** - * @param reader - * @throws IOException - */ - /** - * @param reader - * @throws IOException - */ - private void readAccessTokens(JsonReader reader) throws IOException { - reader.beginArray(); - while (reader.hasNext()) { - OAuth2AccessTokenEntity token = new OAuth2AccessTokenEntity(); - reader.beginObject(); - Long currentId = null; - String clientId = null; - Long authHolderId = null; - Long refreshTokenId = null; - Long idTokenId = null; - Set permissions = new HashSet<>(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ID)) { - currentId = reader.nextLong(); - } else if (name.equals(EXPIRATION)) { - Date date = utcToDate(reader.nextString()); - token.setExpiration(date); - } else if (name.equals(VALUE)) { - String value = reader.nextString(); - try { - // all tokens are JWTs - token.setJwt(JWTParser.parse(value)); - } catch (ParseException ex) { - logger.error("Unable to set refresh token value to {}", value, ex); - } - } else if (name.equals(CLIENT_ID)) { - clientId = reader.nextString(); - } else if (name.equals(AUTHENTICATION_HOLDER_ID)) { - authHolderId = reader.nextLong(); - } else if (name.equals(REFRESH_TOKEN_ID)) { - refreshTokenId = reader.nextLong(); - } else if (name.equals(ID_TOKEN_ID)) { - idTokenId = reader.nextLong(); - } else if (name.equals(SCOPE)) { - Set scope = readSet(reader); - token.setScope(scope); - } else if (name.equals(PERMISSIONS)) { - reader.beginArray(); - while (reader.hasNext()) { - Permission p = new Permission(); - Long rsid = null; - Set scope = new HashSet<>(); - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String pname = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (pname.equals(RESOURCE_SET)) { - rsid = reader.nextLong(); - } else if (pname.equals(SCOPES)) { - scope = readSet(reader); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - p.setScopes(scope); - Permission saved = permissionRepository.saveRawPermission(p); - permissionToResourceRefs.put(saved.getId(), rsid); - permissions.add(saved); - } - reader.endArray(); - token.setPermissions(permissions); - } else if (name.equals(TYPE)) { - token.setTokenType(reader.nextString()); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - Long newId = tokenRepository.saveAccessToken(token).getId(); - accessTokenToClientRefs.put(currentId, clientId); - accessTokenToAuthHolderRefs.put(currentId, authHolderId); - if (refreshTokenId != null) { - accessTokenToRefreshTokenRefs.put(currentId, refreshTokenId); - } - if (idTokenId != null) { - accessTokenToIdTokenRefs.put(currentId, idTokenId); - } - accessTokenOldToNewIdMap.put(currentId, newId); - logger.debug("Read access token {}", currentId); - } - reader.endArray(); - logger.info("Done reading access tokens"); - } - - - private Map authHolderOldToNewIdMap = new HashMap(); - - /** - * @param reader - * @throws IOException - */ - private void readAuthenticationHolders(JsonReader reader) throws IOException { - reader.beginArray(); - while (reader.hasNext()) { - AuthenticationHolderEntity ahe = new AuthenticationHolderEntity(); - reader.beginObject(); - Long currentId = null; - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ID)) { - currentId = reader.nextLong(); - } else if (name.equals(REQUEST_PARAMETERS)) { - ahe.setRequestParameters(readMap(reader)); - } else if (name.equals(CLIENT_ID)) { - ahe.setClientId(reader.nextString()); - } else if (name.equals(SCOPE)) { - ahe.setScope(readSet(reader)); - } else if (name.equals(RESOURCE_IDS)) { - ahe.setResourceIds(readSet(reader)); - } else if (name.equals(AUTHORITIES)) { - Set authorityStrs = readSet(reader); - Set authorities = new HashSet(); - for (String s : authorityStrs) { - GrantedAuthority ga = new SimpleGrantedAuthority(s); - authorities.add(ga); - } - ahe.setAuthorities(authorities); - } else if (name.equals(APPROVED)) { - ahe.setApproved(reader.nextBoolean()); - } else if (name.equals(REDIRECT_URI)) { - ahe.setRedirectUri(reader.nextString()); - } else if (name.equals(RESPONSE_TYPES)) { - ahe.setResponseTypes(readSet(reader)); - } else if (name.equals(EXTENSIONS)) { - ahe.setExtensions(readMap(reader)); - } else if (name.equals(SAVED_USER_AUTHENTICATION)) { - ahe.setUserAuth(readSavedUserAuthentication(reader)); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - Long newId = authHolderRepository.save(ahe).getId(); - authHolderOldToNewIdMap.put(currentId, newId); - logger.debug("Read authentication holder {}", currentId); - } - reader.endArray(); - logger.info("Done reading authentication holders"); - } - - /** - * @param reader - * @return - * @throws IOException - */ - private SavedUserAuthentication readSavedUserAuthentication(JsonReader reader) throws IOException { - SavedUserAuthentication savedUserAuth = new SavedUserAuthentication(); - reader.beginObject(); - - while (reader.hasNext()) { - switch(reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(NAME)) { - savedUserAuth.setName(reader.nextString()); - } else if (name.equals(SOURCE_CLASS)) { - savedUserAuth.setSourceClass(reader.nextString()); - } else if (name.equals(AUTHENTICATED)) { - savedUserAuth.setAuthenticated(reader.nextBoolean()); - } else if (name.equals(AUTHORITIES)) { - Set authorityStrs = readSet(reader); - Set authorities = new HashSet(); - for (String s : authorityStrs) { - GrantedAuthority ga = new SimpleGrantedAuthority(s); - authorities.add(ga); - } - savedUserAuth.setAuthorities(authorities); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - - reader.endObject(); - return savedUserAuth; - } - - private Map grantOldToNewIdMap = new HashMap<>(); - private Map> grantToAccessTokensRefs = new HashMap<>(); - - /** - * @param reader - * @throws IOException - */ - private void readGrants(JsonReader reader) throws IOException { - reader.beginArray(); - while (reader.hasNext()) { - ApprovedSite site = new ApprovedSite(); - Long currentId = null; - Set tokenIds = null; - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ID)) { - currentId = reader.nextLong(); - } else if (name.equals(ACCESS_DATE)) { - Date date = utcToDate(reader.nextString()); - site.setAccessDate(date); - } else if (name.equals(CLIENT_ID)) { - site.setClientId(reader.nextString()); - } else if (name.equals(CREATION_DATE)) { - Date date = utcToDate(reader.nextString()); - site.setCreationDate(date); - } else if (name.equals(TIMEOUT_DATE)) { - Date date = utcToDate(reader.nextString()); - site.setTimeoutDate(date); - } else if (name.equals(USER_ID)) { - site.setUserId(reader.nextString()); - } else if (name.equals(ALLOWED_SCOPES)) { - Set allowedScopes = readSet(reader); - site.setAllowedScopes(allowedScopes); - } else if (name.equals(APPROVED_ACCESS_TOKENS)) { - tokenIds = readSet(reader); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - Long newId = approvedSiteRepository.save(site).getId(); - grantOldToNewIdMap.put(currentId, newId); - if (tokenIds != null) { - grantToAccessTokensRefs.put(currentId, tokenIds); - } - logger.debug("Read grant {}", currentId); - } - reader.endArray(); - logger.info("Done reading grants"); - } - - private Map whitelistedSiteOldToNewIdMap = new HashMap(); - - /** - * @param reader - * @throws IOException - */ - private void readWhitelistedSites(JsonReader reader) throws IOException { - reader.beginArray(); - while (reader.hasNext()) { - WhitelistedSite wlSite = new WhitelistedSite(); - Long currentId = null; - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (name.equals(ID)) { - currentId = reader.nextLong(); - } else if (name.equals(CLIENT_ID)) { - wlSite.setClientId(reader.nextString()); - } else if (name.equals(CREATOR_USER_ID)) { - wlSite.setCreatorUserId(reader.nextString()); - } else if (name.equals(ALLOWED_SCOPES)) { - Set allowedScopes = readSet(reader); - wlSite.setAllowedScopes(allowedScopes); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - Long newId = wlSiteRepository.save(wlSite).getId(); - whitelistedSiteOldToNewIdMap.put(currentId, newId); - } - reader.endArray(); - logger.info("Done reading whitelisted sites"); - } - - /** - * @param reader - * @throws IOException - */ - private void readBlacklistedSites(JsonReader reader) throws IOException { - reader.beginArray(); - while (reader.hasNext()) { - BlacklistedSite blSite = new BlacklistedSite(); - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (name.equals(ID)) { - reader.skipValue(); - } else if (name.equals(URI)) { - blSite.setUri(reader.nextString()); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - blSiteRepository.save(blSite); - } - reader.endArray(); - logger.info("Done reading blacklisted sites"); - } - - /** - * @param reader - * @throws IOException - */ - private void readClients(JsonReader reader) throws IOException { - reader.beginArray(); - while (reader.hasNext()) { - ClientDetailsEntity client = new ClientDetailsEntity(); - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(CLIENT_ID)) { - client.setClientId(reader.nextString()); - } else if (name.equals(RESOURCE_IDS)) { - Set resourceIds = readSet(reader); - client.setResourceIds(resourceIds); - } else if (name.equals(SECRET)) { - client.setClientSecret(reader.nextString()); - } else if (name.equals(SCOPE)) { - Set scope = readSet(reader); - client.setScope(scope); - } else if (name.equals(AUTHORITIES)) { - Set authorityStrs = readSet(reader); - Set authorities = new HashSet(); - for (String s : authorityStrs) { - GrantedAuthority ga = new SimpleGrantedAuthority(s); - authorities.add(ga); - } - client.setAuthorities(authorities); - } else if (name.equals(ACCESS_TOKEN_VALIDITY_SECONDS)) { - client.setAccessTokenValiditySeconds(reader.nextInt()); - } else if (name.equals(REFRESH_TOKEN_VALIDITY_SECONDS)) { - client.setRefreshTokenValiditySeconds(reader.nextInt()); - } else if (name.equals(REDIRECT_URIS)) { - Set redirectUris = readSet(reader); - client.setRedirectUris(redirectUris); - } else if (name.equals(NAME)) { - client.setClientName(reader.nextString()); - } else if (name.equals(URI)) { - client.setClientUri(reader.nextString()); - } else if (name.equals(LOGO_URI)) { - client.setLogoUri(reader.nextString()); - } else if (name.equals(CONTACTS)) { - Set contacts = readSet(reader); - client.setContacts(contacts); - } else if (name.equals(TOS_URI)) { - client.setTosUri(reader.nextString()); - } else if (name.equals(TOKEN_ENDPOINT_AUTH_METHOD)) { - AuthMethod am = AuthMethod.getByValue(reader.nextString()); - client.setTokenEndpointAuthMethod(am); - } else if (name.equals(GRANT_TYPES)) { - Set grantTypes = readSet(reader); - client.setGrantTypes(grantTypes); - } else if (name.equals(RESPONSE_TYPES)) { - Set responseTypes = readSet(reader); - client.setResponseTypes(responseTypes); - } else if (name.equals(POLICY_URI)) { - client.setPolicyUri(reader.nextString()); - } else if (name.equals(APPLICATION_TYPE)) { - AppType appType = AppType.getByValue(reader.nextString()); - client.setApplicationType(appType); - } else if (name.equals(SECTOR_IDENTIFIER_URI)) { - client.setSectorIdentifierUri(reader.nextString()); - } else if (name.equals(SUBJECT_TYPE)) { - SubjectType st = SubjectType.getByValue(reader.nextString()); - client.setSubjectType(st); - } else if (name.equals(JWKS_URI)) { - client.setJwksUri(reader.nextString()); - } else if (name.equals(JWKS)) { - try { - client.setJwks(JWKSet.parse(reader.nextString())); - } catch (ParseException e) { - logger.error("Couldn't parse JWK Set", e); - } - } else if (name.equals(REQUEST_OBJECT_SIGNING_ALG)) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setRequestObjectSigningAlg(alg); - } else if (name.equals(USER_INFO_ENCRYPTED_RESPONSE_ALG)) { - JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); - client.setUserInfoEncryptedResponseAlg(alg); - } else if (name.equals(USER_INFO_ENCRYPTED_RESPONSE_ENC)) { - EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); - client.setUserInfoEncryptedResponseEnc(alg); - } else if (name.equals(USER_INFO_SIGNED_RESPONSE_ALG)) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setUserInfoSignedResponseAlg(alg); - } else if (name.equals(ID_TOKEN_SIGNED_RESPONSE_ALG)) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setIdTokenSignedResponseAlg(alg); - } else if (name.equals(ID_TOKEN_ENCRYPTED_RESPONSE_ALG)) { - JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); - client.setIdTokenEncryptedResponseAlg(alg); - } else if (name.equals(ID_TOKEN_ENCRYPTED_RESPONSE_ENC)) { - EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); - client.setIdTokenEncryptedResponseEnc(alg); - } else if (name.equals(TOKEN_ENDPOINT_AUTH_SIGNING_ALG)) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setTokenEndpointAuthSigningAlg(alg); - } else if (name.equals(DEFAULT_MAX_AGE)) { - client.setDefaultMaxAge(reader.nextInt()); - } else if (name.equals(REQUIRE_AUTH_TIME)) { - client.setRequireAuthTime(reader.nextBoolean()); - } else if (name.equals(DEFAULT_ACR_VALUES)) { - Set defaultACRvalues = readSet(reader); - client.setDefaultACRvalues(defaultACRvalues); - } else if (name.equals("initiateLoginUri")) { - client.setInitiateLoginUri(reader.nextString()); - } else if (name.equals(POST_LOGOUT_REDIRECT_URI)) { - Set postLogoutUris = readSet(reader); - client.setPostLogoutRedirectUris(postLogoutUris); - } else if (name.equals(REQUEST_URIS)) { - Set requestUris = readSet(reader); - client.setRequestUris(requestUris); - } else if (name.equals(DESCRIPTION)) { - client.setClientDescription(reader.nextString()); - } else if (name.equals(ALLOW_INTROSPECTION)) { - client.setAllowIntrospection(reader.nextBoolean()); - } else if (name.equals(REUSE_REFRESH_TOKEN)) { - client.setReuseRefreshToken(reader.nextBoolean()); - } else if (name.equals(CLEAR_ACCESS_TOKENS_ON_REFRESH)) { - client.setClearAccessTokensOnRefresh(reader.nextBoolean()); - } else if (name.equals(DYNAMICALLY_REGISTERED)) { - client.setDynamicallyRegistered(reader.nextBoolean()); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - clientRepository.saveClient(client); - } - reader.endArray(); - logger.info("Done reading clients"); - } - - /** - * Read the list of system scopes from the reader and insert them into the - * scope repository. - * - * @param reader - * @throws IOException - */ - private void readSystemScopes(JsonReader reader) throws IOException { - reader.beginArray(); - while (reader.hasNext()) { - SystemScope scope = new SystemScope(); - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(VALUE)) { - scope.setValue(reader.nextString()); - } else if (name.equals(DESCRIPTION)) { - scope.setDescription(reader.nextString()); - } else if (name.equals(RESTRICTED)) { - scope.setRestricted(reader.nextBoolean()); - } else if (name.equals(DEFAULT_SCOPE)) { - scope.setDefaultScope(reader.nextBoolean()); - } else if (name.equals(ICON)) { - scope.setIcon(reader.nextString()); - } else if (name.equals(STRUCTURED)) { - logger.debug("Found a structured scope, ignoring structure"); - } else if (name.equals(STRUCTURED_PARAMETER)) { - logger.debug("Found a structured scope, ignoring structure"); - } else { - logger.debug("found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } - } - reader.endObject(); - sysScopeRepository.save(scope); - } - reader.endArray(); - logger.info("Done reading system scopes"); - } - - private void fixObjectReferences() { - logger.info("Fixing object references..."); - for (Long oldRefreshTokenId : refreshTokenToClientRefs.keySet()) { - String clientRef = refreshTokenToClientRefs.get(oldRefreshTokenId); - ClientDetailsEntity client = clientRepository.getClientByClientId(clientRef); - Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); - OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); - refreshToken.setClient(client); - tokenRepository.saveRefreshToken(refreshToken); - } - refreshTokenToClientRefs.clear(); - for (Long oldRefreshTokenId : refreshTokenToAuthHolderRefs.keySet()) { - Long oldAuthHolderId = refreshTokenToAuthHolderRefs.get(oldRefreshTokenId); - Long newAuthHolderId = authHolderOldToNewIdMap.get(oldAuthHolderId); - AuthenticationHolderEntity authHolder = authHolderRepository.getById(newAuthHolderId); - Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); - OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); - refreshToken.setAuthenticationHolder(authHolder); - tokenRepository.saveRefreshToken(refreshToken); - } - refreshTokenToAuthHolderRefs.clear(); - for (Long oldAccessTokenId : accessTokenToClientRefs.keySet()) { - String clientRef = accessTokenToClientRefs.get(oldAccessTokenId); - ClientDetailsEntity client = clientRepository.getClientByClientId(clientRef); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); - OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); - accessToken.setClient(client); - tokenRepository.saveAccessToken(accessToken); - } - accessTokenToClientRefs.clear(); - for (Long oldAccessTokenId : accessTokenToAuthHolderRefs.keySet()) { - Long oldAuthHolderId = accessTokenToAuthHolderRefs.get(oldAccessTokenId); - Long newAuthHolderId = authHolderOldToNewIdMap.get(oldAuthHolderId); - AuthenticationHolderEntity authHolder = authHolderRepository.getById(newAuthHolderId); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); - OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); - accessToken.setAuthenticationHolder(authHolder); - tokenRepository.saveAccessToken(accessToken); - } - accessTokenToAuthHolderRefs.clear(); - for (Long oldAccessTokenId : accessTokenToRefreshTokenRefs.keySet()) { - Long oldRefreshTokenId = accessTokenToRefreshTokenRefs.get(oldAccessTokenId); - Long newRefreshTokenId = refreshTokenOldToNewIdMap.get(oldRefreshTokenId); - OAuth2RefreshTokenEntity refreshToken = tokenRepository.getRefreshTokenById(newRefreshTokenId); - Long newAccessTokenId = accessTokenOldToNewIdMap.get(oldAccessTokenId); - OAuth2AccessTokenEntity accessToken = tokenRepository.getAccessTokenById(newAccessTokenId); - accessToken.setRefreshToken(refreshToken); - tokenRepository.saveAccessToken(accessToken); - } - accessTokenToRefreshTokenRefs.clear(); - refreshTokenOldToNewIdMap.clear(); - for (Long oldGrantId : grantToAccessTokensRefs.keySet()) { - Set oldAccessTokenIds = grantToAccessTokensRefs.get(oldGrantId); - - Long newGrantId = grantOldToNewIdMap.get(oldGrantId); - ApprovedSite site = approvedSiteRepository.getById(newGrantId); - - for(Long oldTokenId : oldAccessTokenIds) { - Long newTokenId = accessTokenOldToNewIdMap.get(oldTokenId); - OAuth2AccessTokenEntity token = tokenRepository.getAccessTokenById(newTokenId); - token.setApprovedSite(site); - tokenRepository.saveAccessToken(token); - } - - approvedSiteRepository.save(site); - } - accessTokenOldToNewIdMap.clear(); - grantOldToNewIdMap.clear(); - for (Long permissionId : permissionToResourceRefs.keySet()) { - Long oldResourceId = permissionToResourceRefs.get(permissionId); - Long newResourceId = resourceSetOldToNewIdMap.get(oldResourceId); - Permission p = permissionRepository.getById(permissionId); - ResourceSet rs = resourceSetRepository.getById(newResourceId); - p.setResourceSet(rs); - permissionRepository.saveRawPermission(p); - logger.debug("Mapping rsid " + oldResourceId + " to " + newResourceId + " for permission " + permissionId); - } - permissionToResourceRefs.clear(); - resourceSetOldToNewIdMap.clear(); - - logger.info("Done fixing object references."); - } - -} diff --git a/uma-server/src/main/java/org/mitre/uma/service/impl/UmaDataServiceExtension_1_3.java b/uma-server/src/main/java/org/mitre/uma/service/impl/UmaDataServiceExtension_1_3.java index 41080c4e45..dc58922c34 100644 --- a/uma-server/src/main/java/org/mitre/uma/service/impl/UmaDataServiceExtension_1_3.java +++ b/uma-server/src/main/java/org/mitre/uma/service/impl/UmaDataServiceExtension_1_3.java @@ -17,7 +17,7 @@ package org.mitre.uma.service.impl; -import static org.mitre.util.JsonUtils.*; +import static org.mitre.util.JsonUtils.readSet; import java.io.IOException; import java.util.HashMap; @@ -25,10 +25,13 @@ import java.util.Map; import java.util.Set; +import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.oauth2.model.RegisteredClient; +import org.mitre.oauth2.repository.OAuth2TokenRepository; import org.mitre.openid.connect.ClientDetailsEntityJsonProcessor; import org.mitre.openid.connect.service.MITREidDataService; import org.mitre.openid.connect.service.MITREidDataServiceExtension; +import org.mitre.openid.connect.service.MITREidDataServiceMaps; import org.mitre.openid.connect.service.impl.MITREidDataServiceSupport; import org.mitre.uma.model.Claim; import org.mitre.uma.model.Permission; @@ -42,6 +45,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; import com.google.gson.JsonElement; import com.google.gson.JsonParser; @@ -53,6 +57,7 @@ * @author jricher * */ +@Service("umaDataExtension_1_3") public class UmaDataServiceExtension_1_3 extends MITREidDataServiceSupport implements MITREidDataServiceExtension { private static final String THIS_VERSION = MITREidDataService.MITREID_CONNECT_1_3; @@ -142,8 +147,12 @@ public class UmaDataServiceExtension_1_3 extends MITREidDataServiceSupport imple private static final String CLAIMS_SUPPLIED = "claimsSupplied"; private static final String SAVED_REGISTERED_CLIENTS = "savedRegisteredClients"; private static final String RESOURCE_SETS = "resourceSets"; + private static final String TOKEN_PERMISSIONS = "tokenPermissions"; + private static final String TOKEN_ID = "tokenId"; private static final Logger logger = LoggerFactory.getLogger(UmaDataServiceExtension_1_3.class); + + @Autowired private SavedRegisteredClientService registeredClientService; @@ -151,6 +160,10 @@ public class UmaDataServiceExtension_1_3 extends MITREidDataServiceSupport imple private ResourceSetRepository resourceSetRepository; @Autowired private PermissionRepository permissionRepository; + @Autowired + private OAuth2TokenRepository tokenRepository; + + private Map> tokenToPermissionRefs = new HashMap<>(); /* (non-Javadoc) * @see org.mitre.openid.connect.service.MITREidDataServiceExtension#supportsVersion(java.lang.String) @@ -180,6 +193,40 @@ public void exportExtensionData(JsonWriter writer) throws IOException { writer.beginArray(); writePermissionTickets(writer); writer.endArray(); + + writer.name(TOKEN_PERMISSIONS); + writer.beginArray(); + writeTokenPermissions(writer); + writer.endArray(); + } + + /** + * @param writer + * @throws IOException + */ + private void writeTokenPermissions(JsonWriter writer) throws IOException { + for (OAuth2AccessTokenEntity token : tokenRepository.getAllAccessTokens()) { + if (!token.getPermissions().isEmpty()) { // skip tokens that don't have the permissions structure attached + writer.beginObject(); + writer.name(TOKEN_ID).value(token.getId()); + writer.name(PERMISSIONS); + writer.beginArray(); + for (Permission p : token.getPermissions()) { + writer.beginObject(); + writer.name(RESOURCE_SET).value(p.getResourceSet().getId()); + writer.name(SCOPES); + writer.beginArray(); + for (String s : p.getScopes()) { + writer.value(s); + } + writer.endArray(); + writer.endObject(); + } + writer.endArray(); + + writer.endObject(); + } + } } /** @@ -329,11 +376,83 @@ public boolean importExtensionData(String name, JsonReader reader) throws IOExce } else if (name.equals(PERMISSION_TICKETS)) { readPermissionTickets(reader); return true; + } else if (name.equals(TOKEN_PERMISSIONS)) { + readTokenPermissions(reader); + return true; } else { return false; } } + /** + * @param reader + */ + private void readTokenPermissions(JsonReader reader) throws IOException { + reader.beginArray(); + while(reader.hasNext()) { + reader.beginObject(); + Long tokenId = null; + Set permissions = new HashSet<>(); + while (reader.hasNext()) { + switch(reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (name.equals(TOKEN_ID)) { + tokenId = reader.nextLong(); + } else if (name.equals(PERMISSIONS)) { + reader.beginArray(); + while (reader.hasNext()) { + Permission p = new Permission(); + Long rsid = null; + Set scope = new HashSet<>(); + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String pname = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (pname.equals(RESOURCE_SET)) { + rsid = reader.nextLong(); + } else if (pname.equals(SCOPES)) { + scope = readSet(reader); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + p.setScopes(scope); + Permission saved = permissionRepository.saveRawPermission(p); + permissionToResourceRefs.put(saved.getId(), rsid); + permissions.add(saved.getId()); + } + reader.endArray(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + tokenToPermissionRefs.put(tokenId, permissions); + } + reader.endArray(); + + } + private Map permissionToResourceRefs = new HashMap<>(); /** @@ -626,7 +745,7 @@ private void readSavedRegisteredClients(JsonReader reader) throws IOException{ * @see org.mitre.openid.connect.service.MITREidDataServiceExtension#fixExtensionObjectReferences() */ @Override - public void fixExtensionObjectReferences() { + public void fixExtensionObjectReferences(MITREidDataServiceMaps maps) { for (Long permissionId : permissionToResourceRefs.keySet()) { Long oldResourceId = permissionToResourceRefs.get(permissionId); Long newResourceId = resourceSetOldToNewIdMap.get(oldResourceId); @@ -636,8 +755,22 @@ public void fixExtensionObjectReferences() { permissionRepository.saveRawPermission(p); logger.debug("Mapping rsid " + oldResourceId + " to " + newResourceId + " for permission " + permissionId); } + for (Long tokenId : tokenToPermissionRefs.keySet()) { + Long newTokenId = maps.getAccessTokenOldToNewIdMap().get(tokenId); + OAuth2AccessTokenEntity token = tokenRepository.getAccessTokenById(newTokenId); + + Set permissions = new HashSet<>(); + for (Long permissionId : tokenToPermissionRefs.get(tokenId)) { + Permission p = permissionRepository.getById(permissionId); + permissions.add(p); + } + + token.setPermissions(permissions); + tokenRepository.saveAccessToken(token); + } permissionToResourceRefs.clear(); resourceSetOldToNewIdMap.clear(); + tokenToPermissionRefs.clear(); } } From b176d4d77e442311f51b6cf64f002589120bfd1c Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 16 Feb 2017 18:24:21 -0500 Subject: [PATCH 120/269] cleaned up old endpoints --- .../mitre/oauth2/web/RevocationEndpoint.java | 79 ++++++++++++++----- .../org/mitre/openid/connect/web/DataAPI.java | 3 +- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/RevocationEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/RevocationEndpoint.java index cc9d04ad37..7c9dab0918 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/RevocationEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/RevocationEndpoint.java @@ -16,17 +16,24 @@ *******************************************************************************/ package org.mitre.oauth2.web; -import java.security.Principal; +import static org.mitre.oauth2.web.AuthenticationUtilities.ensureOAuthScope; +import java.util.Collection; + +import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.oauth2.model.OAuth2RefreshTokenEntity; +import org.mitre.oauth2.service.ClientDetailsEntityService; import org.mitre.oauth2.service.OAuth2TokenEntityService; +import org.mitre.oauth2.service.SystemScopeService; import org.mitre.openid.connect.view.HttpCodeView; +import org.mitre.uma.model.ResourceSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; @@ -38,7 +45,10 @@ @Controller public class RevocationEndpoint { @Autowired - OAuth2TokenEntityService tokenServices; + private ClientDetailsEntityService clientService; + + @Autowired + private OAuth2TokenEntityService tokenServices; /** * Logger for this class @@ -49,32 +59,53 @@ public class RevocationEndpoint { @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')") @RequestMapping("/" + URL) - public String revoke(@RequestParam("token") String tokenValue, @RequestParam(value = "token_type_hint", required = false) String tokenType, Principal principal, Model model) { + public String revoke(@RequestParam("token") String tokenValue, @RequestParam(value = "token_type_hint", required = false) String tokenType, Authentication auth, Model model) { // This is the token as passed in from OAuth (in case we need it some day) //OAuth2AccessTokenEntity tok = tokenServices.getAccessToken((OAuth2Authentication) principal); - OAuth2Request authRequest = null; - if (principal instanceof OAuth2Authentication) { - // if the client is acting on its own behalf (the common case), pull out the client authorization request - authRequest = ((OAuth2Authentication) principal).getOAuth2Request(); + ClientDetailsEntity authClient = null; + + if (auth instanceof OAuth2Authentication) { + // the client authenticated with OAuth, do our UMA checks + ensureOAuthScope(auth, SystemScopeService.UMA_PROTECTION_SCOPE); + // get out the client that was issued the access token (not the token being revoked) + OAuth2Authentication o2a = (OAuth2Authentication) auth; + + String authClientId = o2a.getOAuth2Request().getClientId(); + authClient = clientService.loadClientByClientId(authClientId); + + // the owner is the user who authorized the token in the first place + String ownerId = o2a.getUserAuthentication().getName(); + + } else { + // the client authenticated directly, make sure it's got the right access + + String authClientId = auth.getName(); // direct authentication puts the client_id into the authentication's name field + authClient = clientService.loadClientByClientId(authClientId); + } try { // check and handle access tokens first OAuth2AccessTokenEntity accessToken = tokenServices.readAccessToken(tokenValue); - if (authRequest != null) { - // client acting on its own, make sure it owns the token - if (!accessToken.getClient().getClientId().equals(authRequest.getClientId())) { - // trying to revoke a token we don't own, throw a 403 - model.addAttribute(HttpCodeView.CODE, HttpStatus.FORBIDDEN); - return HttpCodeView.VIEWNAME; - } + + // client acting on its own, make sure it owns the token + if (!accessToken.getClient().getClientId().equals(authClient.getClientId())) { + // trying to revoke a token we don't own, throw a 403 + + logger.info("Client " + authClient.getClientId() + " tried to revoke a token owned by " + accessToken.getClient().getClientId()); + + model.addAttribute(HttpCodeView.CODE, HttpStatus.FORBIDDEN); + return HttpCodeView.VIEWNAME; } // if we got this far, we're allowed to do this tokenServices.revokeAccessToken(accessToken); + + logger.debug("Client " + authClient.getClientId() + " revoked access token " + tokenValue); + model.addAttribute(HttpCodeView.CODE, HttpStatus.OK); return HttpCodeView.VIEWNAME; @@ -84,17 +115,21 @@ public String revoke(@RequestParam("token") String tokenValue, @RequestParam(val try { OAuth2RefreshTokenEntity refreshToken = tokenServices.getRefreshToken(tokenValue); - if (authRequest != null) { - // client acting on its own, make sure it owns the token - if (!refreshToken.getClient().getClientId().equals(authRequest.getClientId())) { - // trying to revoke a token we don't own, throw a 403 - model.addAttribute(HttpCodeView.CODE, HttpStatus.FORBIDDEN); - return HttpCodeView.VIEWNAME; - } + // client acting on its own, make sure it owns the token + if (!refreshToken.getClient().getClientId().equals(authClient.getClientId())) { + // trying to revoke a token we don't own, throw a 403 + + logger.info("Client " + authClient.getClientId() + " tried to revoke a token owned by " + refreshToken.getClient().getClientId()); + + model.addAttribute(HttpCodeView.CODE, HttpStatus.FORBIDDEN); + return HttpCodeView.VIEWNAME; } // if we got this far, we're allowed to do this tokenServices.revokeRefreshToken(refreshToken); + + logger.debug("Client " + authClient.getClientId() + " revoked access token " + tokenValue); + model.addAttribute(HttpCodeView.CODE, HttpStatus.OK); return HttpCodeView.VIEWNAME; @@ -102,6 +137,8 @@ public String revoke(@RequestParam("token") String tokenValue, @RequestParam(val // neither token type was found, simply say "OK" and be on our way. + logger.debug("Failed to revoke token " + tokenValue); + model.addAttribute(HttpCodeView.CODE, HttpStatus.OK); return HttpCodeView.VIEWNAME; } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java index 39b4584b01..8f2667a3da 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java @@ -111,12 +111,13 @@ public String importData(Reader in, Model m) throws IOException { } break; case END_OBJECT: - reader.endObject(); break; case END_DOCUMENT: break; } } + + reader.endObject(); return "httpCodeView"; } From 907f94e0ce2e427df44229a47dfe9fbd7c24b49d Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 17 Feb 2017 15:11:10 -0500 Subject: [PATCH 121/269] collapsed tags into relevant pages --- .../src/main/webapp/WEB-INF/tags/aboutContent.tag | 5 ----- .../src/main/webapp/WEB-INF/tags/contactContent.tag | 5 ----- .../src/main/webapp/WEB-INF/tags/statsContent.tag | 8 -------- .../src/main/webapp/WEB-INF/views/about.jsp | 5 ++++- .../src/main/webapp/WEB-INF/views/contact.jsp | 5 ++++- .../src/main/webapp/WEB-INF/views/stats.jsp | 8 +++++++- .../src/main/webapp/resources/static_resources_go_here | 0 7 files changed, 15 insertions(+), 21 deletions(-) delete mode 100644 openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/aboutContent.tag delete mode 100644 openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/contactContent.tag delete mode 100644 openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/statsContent.tag delete mode 100644 openid-connect-server-webapp/src/main/webapp/resources/static_resources_go_here diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/aboutContent.tag b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/aboutContent.tag deleted file mode 100644 index 4d4ca8bb03..0000000000 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/aboutContent.tag +++ /dev/null @@ -1,5 +0,0 @@ -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -

-

- -

\ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/contactContent.tag b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/contactContent.tag deleted file mode 100644 index 4f978c4d3f..0000000000 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/contactContent.tag +++ /dev/null @@ -1,5 +0,0 @@ -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -

-

- -

diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/statsContent.tag b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/statsContent.tag deleted file mode 100644 index 9e529ff684..0000000000 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/statsContent.tag +++ /dev/null @@ -1,8 +0,0 @@ -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -

- -

- - - -

diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/about.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/about.jsp index 2661f2b3e7..1bf74f8b5e 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/about.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/about.jsp @@ -15,7 +15,10 @@
- +

+

+ +

diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/contact.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/contact.jsp index 4e285f28de..cdfcdbcd16 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/contact.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/contact.jsp @@ -15,7 +15,10 @@
- +

+

+ +

diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/stats.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/stats.jsp index 16b75ca420..8e97e6b368 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/stats.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/stats.jsp @@ -14,7 +14,13 @@
- +

+ +

+ + + +

diff --git a/openid-connect-server-webapp/src/main/webapp/resources/static_resources_go_here b/openid-connect-server-webapp/src/main/webapp/resources/static_resources_go_here deleted file mode 100644 index e69de29bb2..0000000000 From 606dd2633bcf91b4e30a4eff544dce13f75e51ca Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 17 Feb 2017 15:11:45 -0500 Subject: [PATCH 122/269] collapsed home page tags --- .../webapp/WEB-INF/tags/landingPageAbout.tag | 6 --- .../WEB-INF/tags/landingPageContact.tag | 5 -- .../webapp/WEB-INF/tags/landingPageStats.tag | 27 ---------- .../WEB-INF/tags/landingPageWelcome.tag | 9 ---- .../src/main/webapp/WEB-INF/views/home.jsp | 51 +++++++++++++++++-- 5 files changed, 47 insertions(+), 51 deletions(-) delete mode 100644 openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageAbout.tag delete mode 100644 openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageContact.tag delete mode 100644 openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageStats.tag delete mode 100644 openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageWelcome.tag diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageAbout.tag b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageAbout.tag deleted file mode 100644 index 0d9a4e6ac4..0000000000 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageAbout.tag +++ /dev/null @@ -1,6 +0,0 @@ -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -

- -

- -

»

diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageContact.tag b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageContact.tag deleted file mode 100644 index db9d3f51e5..0000000000 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageContact.tag +++ /dev/null @@ -1,5 +0,0 @@ -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -

-

- -

diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageStats.tag b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageStats.tag deleted file mode 100644 index 45e3527805..0000000000 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageStats.tag +++ /dev/null @@ -1,27 +0,0 @@ -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -

- -

- -

- - - -

- - \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageWelcome.tag b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageWelcome.tag deleted file mode 100644 index 851ddbe0b2..0000000000 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/landingPageWelcome.tag +++ /dev/null @@ -1,9 +0,0 @@ -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> -
-
- -
-

-

-
-
\ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/home.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/home.jsp index cd2102719a..fc83dabedb 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/home.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/home.jsp @@ -12,15 +12,29 @@
- +
+
+ +
+

+

+
+
- +

+ +

+ +

»

- +

+

+ +

@@ -28,11 +42,40 @@
- +

+ +

+ +

+ + + +

+ + + + From c79b6da9d9ae10cf64ff7c91b17dd2e9b3d60c68 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 17 Feb 2017 17:34:03 -0500 Subject: [PATCH 123/269] Javascript files for UI functionality loaded from configuration bean --- .../connect/config/UIConfiguration.java | 59 +++++++++++++++++++ .../webapp/WEB-INF/application-context.xml | 3 + .../src/main/webapp/WEB-INF/tags/footer.tag | 23 +++++--- .../src/main/webapp/WEB-INF/ui-config.xml | 57 ++++++++++++++++++ .../connect/web/ServerConfigInterceptor.java | 5 ++ 5 files changed, 138 insertions(+), 9 deletions(-) create mode 100644 openid-connect-common/src/main/java/org/mitre/openid/connect/config/UIConfiguration.java create mode 100644 openid-connect-server-webapp/src/main/webapp/WEB-INF/ui-config.xml diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/UIConfiguration.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/UIConfiguration.java new file mode 100644 index 0000000000..7bc689d847 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/UIConfiguration.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.openid.connect.config; + +import java.util.Set; + +/** + * + * Bean for UI (front-end) configuration to be read at start-up. + * + * @author jricher + * + */ +public class UIConfiguration { + + private Set jsFiles; + private Set templateFiles; + + /** + * @return the jsFiles + */ + public Set getJsFiles() { + return jsFiles; + } + /** + * @param jsFiles the jsFiles to set + */ + public void setJsFiles(Set jsFiles) { + this.jsFiles = jsFiles; + } + /** + * @return the templateFiles + */ + public Set getTemplateFiles() { + return templateFiles; + } + /** + * @param templateFiles the templateFiles to set + */ + public void setTemplateFiles(Set templateFiles) { + this.templateFiles = templateFiles; + } + +} diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml index 0727daf120..bdbf0c52cc 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml @@ -258,6 +258,9 @@ + + + diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag index e1f35831f1..3834c2c3de 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag @@ -15,6 +15,16 @@ + @@ -22,17 +32,12 @@ + - - - - - - - - + + + - diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/ui-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/ui-config.xml new file mode 100644 index 0000000000..0813a73c84 --- /dev/null +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/ui-config.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + resources/js/client.js + resources/js/grant.js + resources/js/scope.js + resources/js/whitelist.js + resources/js/dynreg.js + resources/js/rsreg.js + resources/js/token.js + resources/js/blacklist.js + + + + + + + + + + + + diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ServerConfigInterceptor.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ServerConfigInterceptor.java index c2c6a50ba8..321d7b7633 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ServerConfigInterceptor.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ServerConfigInterceptor.java @@ -23,6 +23,7 @@ import javax.servlet.http.HttpServletResponse; import org.mitre.openid.connect.config.ConfigurationPropertiesBean; +import org.mitre.openid.connect.config.UIConfiguration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; @@ -38,10 +39,14 @@ public class ServerConfigInterceptor extends HandlerInterceptorAdapter { @Autowired private ConfigurationPropertiesBean config; + + @Autowired + private UIConfiguration ui; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { request.setAttribute("config", config); + request.setAttribute("ui", ui); return true; } From 00c4ea9199df8b8b940d92c2bc0a02b124dbafe5 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sat, 18 Feb 2017 18:23:15 -0500 Subject: [PATCH 124/269] dynamically load client routes from sub page --- .../src/main/webapp/resources/js/admin.js | 169 ++---------------- .../src/main/webapp/resources/js/client.js | 157 ++++++++++++++++ 2 files changed, 175 insertions(+), 151 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js b/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js index 343fb3e7bb..4167db2200 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js @@ -415,6 +415,7 @@ var ErrorHandlerView = Backbone.View.extend({ var AppRouter = Backbone.Router.extend({ routes:{ + /* "admin/clients":"listClients", "admin/client/new":"newClient", "admin/client/:id":"editClient", @@ -440,7 +441,8 @@ var AppRouter = Backbone.Router.extend({ "dev/resource":"resReg", "dev/resource/new":"newResReg", "dev/resource/edit":"editResReg", - + */ + "": "root" }, @@ -472,158 +474,9 @@ var AppRouter = Backbone.Router.extend({ this.errorHandlerView = new ErrorHandlerView(); - var base = $('base').attr('href'); - $.getJSON(base + '.well-known/openid-configuration', function(data) { - app.serverConfiguration = data; - var baseUrl = $.url(app.serverConfiguration.issuer); - Backbone.history.start({pushState: true, root: baseUrl.attr('relative') + 'manage/'}); - }); - - }, - - listClients:function () { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('client.manage'), href:"manage/#admin/clients"} - ]); - - this.updateSidebar('admin/clients'); - - var view = new ClientListView({model:this.clientList, stats: this.clientStats, systemScopeList: this.systemScopeList, whiteListList: this.whiteListList}); - - view.load(function() { - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('client.manage')); - }); - - }, - - newClient:function() { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('client.manage'), href:"manage/#admin/clients"}, - {text:$.t('client.client-form.new'), href:""} - ]); - - this.updateSidebar('admin/clients'); - - var client = new ClientModel(); - - var view = new ClientFormView({model:client, systemScopeList: this.systemScopeList}); - view.load(function() { - var userInfo = getUserInfo(); - var contacts = []; - if (userInfo != null && userInfo.email != null) { - contacts.push(userInfo.email); - } - - // use a different set of defaults based on heart mode flag - if (heartMode) { - client.set({ - tokenEndpointAuthMethod: "PRIVATE_KEY", - generateClientSecret:false, - displayClientSecret:false, - requireAuthTime:true, - defaultMaxAge:60000, - scope: _.uniq(_.flatten(app.systemScopeList.defaultScopes().pluck("value"))), - accessTokenValiditySeconds:3600, - refreshTokenValiditySeconds:24*3600, - idTokenValiditySeconds:300, - grantTypes: ["authorization_code"], - responseTypes: ["code"], - subjectType: "PUBLIC", - jwksType: "URI", - contacts: contacts - }, { silent: true }); - } else { - // set up this new client to require a secret and have us autogenerate one - client.set({ - tokenEndpointAuthMethod: "SECRET_BASIC", - generateClientSecret:true, - displayClientSecret:false, - requireAuthTime:true, - defaultMaxAge:60000, - scope: _.uniq(_.flatten(app.systemScopeList.defaultScopes().pluck("value"))), - accessTokenValiditySeconds:3600, - idTokenValiditySeconds:600, - grantTypes: ["authorization_code"], - responseTypes: ["code"], - subjectType: "PUBLIC", - jwksType: "URI", - contacts: contacts - }, { silent: true }); - } - - - $('#content').html(view.render().el); - setPageTitle($.t('client.client-form.new')); - }); }, - editClient:function(id) { - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('client.manage'), href:"manage/#admin/clients"}, - {text:$.t('client.client-form.edit'), href:"manage/#admin/client/" + id} - ]); - - this.updateSidebar('admin/clients'); - - var client = this.clientList.get(id); - if (!client) { - client = new ClientModel({id:id}); - } - - var view = new ClientFormView({model:client, systemScopeList: app.systemScopeList}); - view.load(function() { - if ($.inArray("refresh_token", client.get("grantTypes")) != -1) { - client.set({ - allowRefresh: true - }, { silent: true }); - } - - if (client.get("jwks")) { - client.set({ - jwksType: "VAL" - }, { silent: true }); - } else { - client.set({ - jwksType: "URI" - }, { silent: true }); - } - - client.set({ - generateClientSecret:false, - displayClientSecret:false - }, { silent: true }); - - $('#content').html(view.render().el); - setPageTitle($.t('client.client-form.edit')); - }); - - }, whiteList:function () { @@ -1082,6 +935,13 @@ $(function () { $.ajaxSetup({cache:false}); app = new AppRouter(); + console.log(ui.routes); + + _.each(ui.routes.reverse(), function(route) { + console.log("Adding route: " + route.name); + app.route(route.path, route.name, route.callback); + }); + app.on('route', function(name, args) { // scroll to top of page on new route selection $("html, body").animate({ scrollTop: 0 }, "slow"); @@ -1091,7 +951,14 @@ $(function () { $(document).on('click', 'a[href^="manage/#"]', function(event) { event.preventDefault(); app.navigate(this.hash.slice(1), {trigger: true}); - }); + }); + + var base = $('base').attr('href'); + $.getJSON(base + '.well-known/openid-configuration', function(data) { + app.serverConfiguration = data; + var baseUrl = $.url(app.serverConfiguration.issuer); + Backbone.history.start({pushState: true, root: baseUrl.attr('relative') + 'manage/'}); + }); }); window.onerror = function ( message, filename, lineno, colno, error ){ diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js index 0dc52738cb..7c6839d2b7 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js @@ -1165,3 +1165,160 @@ var ClientFormView = Backbone.View.extend({ }); +// add routes to the app +/* +"admin/clients":"listClients", +"admin/client/new":"newClient", +"admin/client/:id":"editClient", +*/ + +ui.routes.push({path: "admin/clients", name: "listClients", callback: + function () { + + if (!isAdmin()) { + this.root(); + return; + } + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('client.manage'), href:"manage/#admin/clients"} + ]); + + this.updateSidebar('admin/clients'); + + var view = new ClientListView({model:this.clientList, stats: this.clientStats, systemScopeList: this.systemScopeList, whiteListList: this.whiteListList}); + + view.load(function() { + $('#content').html(view.render().el); + view.delegateEvents(); + setPageTitle($.t('client.manage')); + }); + + } + +}); + +ui.routes.push({path: "admin/client/new", name: "newClient", callback: + function() { + console.log("newClient"); + if (!isAdmin()) { + this.root(); + return; + } + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('client.manage'), href:"manage/#admin/clients"}, + {text:$.t('client.client-form.new'), href:""} + ]); + + this.updateSidebar('admin/clients'); + + var client = new ClientModel(); + + var view = new ClientFormView({model:client, systemScopeList: this.systemScopeList}); + view.load(function() { + var userInfo = getUserInfo(); + var contacts = []; + if (userInfo != null && userInfo.email != null) { + contacts.push(userInfo.email); + } + + // use a different set of defaults based on heart mode flag + if (heartMode) { + client.set({ + tokenEndpointAuthMethod: "PRIVATE_KEY", + generateClientSecret:false, + displayClientSecret:false, + requireAuthTime:true, + defaultMaxAge:60000, + scope: _.uniq(_.flatten(app.systemScopeList.defaultScopes().pluck("value"))), + accessTokenValiditySeconds:3600, + refreshTokenValiditySeconds:24*3600, + idTokenValiditySeconds:300, + grantTypes: ["authorization_code"], + responseTypes: ["code"], + subjectType: "PUBLIC", + jwksType: "URI", + contacts: contacts + }, { silent: true }); + } else { + // set up this new client to require a secret and have us autogenerate one + client.set({ + tokenEndpointAuthMethod: "SECRET_BASIC", + generateClientSecret:true, + displayClientSecret:false, + requireAuthTime:true, + defaultMaxAge:60000, + scope: _.uniq(_.flatten(app.systemScopeList.defaultScopes().pluck("value"))), + accessTokenValiditySeconds:3600, + idTokenValiditySeconds:600, + grantTypes: ["authorization_code"], + responseTypes: ["code"], + subjectType: "PUBLIC", + jwksType: "URI", + contacts: contacts + }, { silent: true }); + } + + + $('#content').html(view.render().el); + setPageTitle($.t('client.client-form.new')); + }); + } +}); + +ui.routes.push({path: "admin/client/:id", name: "editClient", callback: + function(id) { + console.log("editClient " + id); + if (!isAdmin()) { + this.root(); + return; + } + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('client.manage'), href:"manage/#admin/clients"}, + {text:$.t('client.client-form.edit'), href:"manage/#admin/client/" + id} + ]); + + this.updateSidebar('admin/clients'); + + var client = this.clientList.get(id); + if (!client) { + client = new ClientModel({id:id}); + } + + var view = new ClientFormView({model:client, systemScopeList: app.systemScopeList}); + view.load(function() { + if ($.inArray("refresh_token", client.get("grantTypes")) != -1) { + client.set({ + allowRefresh: true + }, { silent: true }); + } + + if (client.get("jwks")) { + client.set({ + jwksType: "VAL" + }, { silent: true }); + } else { + client.set({ + jwksType: "URI" + }, { silent: true }); + } + + client.set({ + generateClientSecret:false, + displayClientSecret:false + }, { silent: true }); + + $('#content').html(view.render().el); + setPageTitle($.t('client.client-form.edit')); + }); + + } +}); From 5af98e11063f291c25eb9548295630acf7320697 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 20 Feb 2017 10:55:22 -0500 Subject: [PATCH 125/269] modularized javascript loading and UI components --- .../src/main/webapp/WEB-INF/ui-config.xml | 1 + .../src/main/webapp/resources/js/admin.js | 444 ------------------ .../src/main/webapp/resources/js/blacklist.js | 28 ++ .../src/main/webapp/resources/js/dynreg.js | 95 ++++ .../src/main/webapp/resources/js/grant.js | 20 + .../src/main/webapp/resources/js/profile.js | 18 + .../src/main/webapp/resources/js/rsreg.js | 78 ++- .../src/main/webapp/resources/js/scope.js | 89 +++- .../src/main/webapp/resources/js/token.js | 23 + .../src/main/webapp/resources/js/whitelist.js | 109 +++++ 10 files changed, 459 insertions(+), 446 deletions(-) create mode 100644 openid-connect-server-webapp/src/main/webapp/resources/js/profile.js diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/ui-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/ui-config.xml index 0813a73c84..2c18786e9d 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/ui-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/ui-config.xml @@ -43,6 +43,7 @@ resources/js/rsreg.js resources/js/token.js resources/js/blacklist.js + resources/js/profile.js diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js b/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js index 4167db2200..f57e884a2f 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js @@ -415,33 +415,6 @@ var ErrorHandlerView = Backbone.View.extend({ var AppRouter = Backbone.Router.extend({ routes:{ - /* - "admin/clients":"listClients", - "admin/client/new":"newClient", - "admin/client/:id":"editClient", - - "admin/whitelists":"whiteList", - "admin/whitelist/new/:cid":"newWhitelist", - "admin/whitelist/:id":"editWhitelist", - - "admin/blacklist":"blackList", - - "admin/scope":"siteScope", - "admin/scope/new":"newScope", - "admin/scope/:id":"editScope", - - "user/approved":"approvedSites", - "user/tokens":"tokens", - "user/profile":"profile", - - "dev/dynreg":"dynReg", - "dev/dynreg/new":"newDynReg", - "dev/dynreg/edit":"editDynReg", - - "dev/resource":"resReg", - "dev/resource/new":"newResReg", - "dev/resource/edit":"editResReg", - */ "": "root" @@ -476,147 +449,6 @@ var AppRouter = Backbone.Router.extend({ }, - - - whiteList:function () { - - if (!isAdmin()) { - this.root(); - return; - } - - this.updateSidebar('admin/whitelists'); - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('whitelist.manage'), href:"manage/#admin/whitelists"} - ]); - - var view = new WhiteListListView({model:this.whiteListList, clientList: this.clientList, systemScopeList: this.systemScopeList}); - - view.load( - function() { - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('whitelist.manage')); - } - ); - - - }, - - newWhitelist:function(cid) { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('whitelist.manage'), href:"manage/#admin/whitelists"}, - {text:$.t('whitelist.new'), href:"manage/#admin/whitelist/new/" + cid} - ]); - - this.updateSidebar('admin/whitelists'); - - var whiteList = new WhiteListModel(); - - var client = this.clientList.get(cid); - if (!client) { - client = new ClientModel({id: cid}); - } - - var view = new WhiteListFormView({model: whiteList, client: client, systemScopeList: this.systemScopeList}); - - view.load( - function() { - - // set the scopes on the model now that everything's loaded - whiteList.set({allowedScopes: client.get('scope')}, {silent: true}); - - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('whitelist.manage')); - } - ); - - }, - - editWhitelist:function(id) { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('whitelist.manage'), href:"manage/#admin/whitelists"}, - {text:$.t('whitelist.edit'), href:"manage/#admin/whitelist/" + id} - ]); - - this.updateSidebar('admin/whitelists'); - - var whiteList = this.whiteListList.get(id); - if (!whiteList) { - whiteList = new WhiteListModel({id: id}); - } - - var view = new WhiteListFormView({model: whiteList, clientList: this.clientList, systemScopeList: this.systemScopeList}); - - view.load( - function() { - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('whitelist.manage')); - } - ); - - }, - - approvedSites:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('grant.manage-approved-sites'), href:"manage/#user/approve"} - ]); - - this.updateSidebar('user/approved'); - - var view = new ApprovedSiteListView({model:this.approvedSiteList, clientList: this.clientList, systemScopeList: this.systemScopeList}); - view.load( - function(collection, response, options) { - $('#content').html(view.render().el); - setPageTitle($.t('grant.manage-approved-sites')); - } - ); - - }, - - tokens:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('token.manage'), href:"manage/#user/tokens"} - ]); - - this.updateSidebar('user/tokens'); - - var view = new TokenListView({model: {access: this.accessTokensList, refresh: this.refreshTokensList}, clientList: this.clientList, systemScopeList: this.systemScopeList}); - - view.load( - function(collection, response, options) { - $('#content').html(view.render().el); - setPageTitle($.t('token.manage')); - } - ); - - }, - notImplemented:function(){ this.breadCrumbView.collection.reset(); this.breadCrumbView.collection.add([ @@ -627,281 +459,7 @@ var AppRouter = Backbone.Router.extend({ $('#content').html("

Not implemented yet.

"); }, - - blackList:function() { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.manage-blacklist'), href:"manage/#admin/blacklist"} - ]); - - this.updateSidebar('admin/blacklist'); - - var view = new BlackListListView({collection: this.blackListList}); - view.load( - function(collection, response, options) { - $('#content').html(view.render().el); - setPageTitle($.t('admin.manage-blacklist')); - } - ); - }, - - siteScope:function() { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('scope.manage'), href:"manage/#admin/scope"} - ]); - - this.updateSidebar('admin/scope'); - - var view = new SystemScopeListView({model:this.systemScopeList}); - - view.load(function() { - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('scope.manage')); - }); - - }, - - newScope:function() { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('scope.manage'), href:"manage/#admin/scope"}, - {text:$.t('scope.system-scope-form.new'), href:"manage/#admin/scope/new"} - ]); - - this.updateSidebar('admin/scope'); - - var scope = new SystemScopeModel(); - - var view = new SystemScopeFormView({model:scope}); - view.load(function() { - $('#content').html(view.render().el); - setPageTitle($.t('scope.system-scope-form.new')); - }); - - }, - - editScope:function(sid) { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('scope.manage'), href:"manage/#admin/scope"}, - {text:$.t('scope.system-scope-form.edit'), href:"manage/#admin/scope/" + sid} - ]); - - this.updateSidebar('admin/scope'); - - var scope = this.systemScopeList.get(sid); - if (!scope) { - scope = new SystemScopeModel({id: sid}); - } - - var view = new SystemScopeFormView({model:scope}); - view.load(function() { - $('#content').html(view.render().el); - setPageTitle($.t('scope.system-scope-form.new')); - }); - - }, - - dynReg:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-client'), href:"manage/#dev/dynreg"} - ]); - - var view = new DynRegRootView({systemScopeList: this.systemScopeList}); - - this.updateSidebar('dev/dynreg'); - - view.load(function() { - $('#content').html(view.render().el); - - setPageTitle($.t('admin.self-service-client')); - }); - - }, - - newDynReg:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-client'), href:"manage/#dev/dynreg"}, - {text:$.t('dynreg.new-client'), href:"manage/#dev/dynreg/new"} - ]); - - this.updateSidebar('dev/dynreg'); - - var client = new DynRegClient(); - var view = new DynRegEditView({model: client, systemScopeList:this.systemScopeList}); - - view.load(function() { - - var userInfo = getUserInfo(); - var contacts = []; - if (userInfo != null && userInfo.email != null) { - contacts.push(userInfo.email); - } - - if (heartMode) { - client.set({ - require_auth_time:true, - default_max_age:60000, - scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), - token_endpoint_auth_method: 'private_key_jwt', - grant_types: ["authorization_code"], - response_types: ["code"], - subject_type: "public", - contacts: contacts - }, { silent: true }); - } else { - client.set({ - require_auth_time:true, - default_max_age:60000, - scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), - token_endpoint_auth_method: 'client_secret_basic', - grant_types: ["authorization_code"], - response_types: ["code"], - subject_type: "public", - contacts: contacts - }, { silent: true }); - } - - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('dynreg.new-client')); - - }); - - }, - - editDynReg:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-client'), href:"manage/#dev/dynreg"}, - {text:$.t('dynreg.edit-existing'), href:"manage/#dev/dynreg/edit"} - ]); - - this.updateSidebar('dev/dynreg'); - - setPageTitle($.t('dynreg.edit-existing')); - // note that this doesn't actually load the client, that's supposed to happen elsewhere... - }, - - resReg:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-resource'), href:"manage/#dev/resource"} - ]); - - this.updateSidebar('dev/resource'); - - var view = new ResRegRootView({systemScopeList: this.systemScopeList}); - view.load(function() { - $('#content').html(view.render().el); - - setPageTitle($.t('admin.self-service-resource')); - }); - - }, - - newResReg:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-resource'), href:"manage/#dev/resource"}, - {text:$.t('rsreg.new'), href:"manage/#dev/resource/new"} - ]); - - this.updateSidebar('dev/resource'); - - var client = new ResRegClient(); - var view = new ResRegEditView({model: client, systemScopeList:this.systemScopeList}); - - view.load(function() { - - var userInfo = getUserInfo(); - var contacts = []; - if (userInfo != null && userInfo.email != null) { - contacts.push(userInfo.email); - } - - client.set({ - scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), - token_endpoint_auth_method: 'client_secret_basic', - contacts: contacts - }, { silent: true }); - - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('rsreg.new')); - - }); - - }, - - editResReg:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-resource'), href:"manage/#dev/resource"}, - {text:$.t('rsreg.edit'), href:"manage/#dev/resource/edit"} - ]); - - this.updateSidebar('dev/resource'); - - setPageTitle($.t('rsreg.edit')); - // note that this doesn't actually load the client, that's supposed to happen elsewhere... - }, - - profile:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.user-profile.show'), href:"manage/#user/profile"} - ]); - - this.updateSidebar('user/profile'); - - var view = new UserProfileView({model: getUserInfo()}); - $('#content').html(view.render().el); - - setPageTitle($.t('admin.user-profile.show')); - - }, - updateSidebar:function(item) { $('.sidebar-nav li.active').removeClass('active'); @@ -935,8 +493,6 @@ $(function () { $.ajaxSetup({cache:false}); app = new AppRouter(); - console.log(ui.routes); - _.each(ui.routes.reverse(), function(route) { console.log("Adding route: " + route.name); app.route(route.path, route.name, route.callback); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js b/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js index f13296a9bd..df97d056b4 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js @@ -180,3 +180,31 @@ var BlackListWidgetView = Backbone.View.extend({ }); + +ui.routes.push({path: "admin/blacklist", name: "blackList", callback: + function() { + + if (!isAdmin()) { + this.root(); + return; + } + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('admin.manage-blacklist'), href:"manage/#admin/blacklist"} + ]); + + this.updateSidebar('admin/blacklist'); + + var view = new BlackListListView({collection: this.blackListList}); + + view.load( + function(collection, response, options) { + $('#content').html(view.render().el); + setPageTitle($.t('admin.manage-blacklist')); + } + ); + } + +}); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js b/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js index 96ad591023..e0bb23b7e0 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js @@ -611,3 +611,98 @@ var DynRegEditView = Backbone.View.extend({ } }); + +ui.routes.push({path: "dev/dynreg", name: "dynReg", callback: + function() { + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('admin.self-service-client'), href:"manage/#dev/dynreg"} + ]); + + var view = new DynRegRootView({systemScopeList: this.systemScopeList}); + + this.updateSidebar('dev/dynreg'); + + view.load(function() { + $('#content').html(view.render().el); + + setPageTitle($.t('admin.self-service-client')); + }); + + } +}); + +ui.routes.push({path: "dev/dynreg/new", name: "newDynReg", callback: + function() { + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('admin.self-service-client'), href:"manage/#dev/dynreg"}, + {text:$.t('dynreg.new-client'), href:"manage/#dev/dynreg/new"} + ]); + + this.updateSidebar('dev/dynreg'); + + var client = new DynRegClient(); + var view = new DynRegEditView({model: client, systemScopeList:this.systemScopeList}); + + view.load(function() { + + var userInfo = getUserInfo(); + var contacts = []; + if (userInfo != null && userInfo.email != null) { + contacts.push(userInfo.email); + } + + if (heartMode) { + client.set({ + require_auth_time:true, + default_max_age:60000, + scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), + token_endpoint_auth_method: 'private_key_jwt', + grant_types: ["authorization_code"], + response_types: ["code"], + subject_type: "public", + contacts: contacts + }, { silent: true }); + } else { + client.set({ + require_auth_time:true, + default_max_age:60000, + scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), + token_endpoint_auth_method: 'client_secret_basic', + grant_types: ["authorization_code"], + response_types: ["code"], + subject_type: "public", + contacts: contacts + }, { silent: true }); + } + + $('#content').html(view.render().el); + view.delegateEvents(); + setPageTitle($.t('dynreg.new-client')); + + }); + + } +}); + +ui.routes.push({path: "dev/dynreg/edit", name: "editDynReg", callback: + function() { + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('admin.self-service-client'), href:"manage/#dev/dynreg"}, + {text:$.t('dynreg.edit-existing'), href:"manage/#dev/dynreg/edit"} + ]); + + this.updateSidebar('dev/dynreg'); + + setPageTitle($.t('dynreg.edit-existing')); + // note that this doesn't actually load the client, that's supposed to happen elsewhere... + } +}); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js b/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js index 03d86fc5c6..303963b4da 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js @@ -258,3 +258,23 @@ var ApprovedSiteView = Backbone.View.extend({ } }); +ui.routes.push({path: "user/approved", name: "approvedSites", callback: + + function() { + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('grant.manage-approved-sites'), href:"manage/#user/approve"} + ]); + + this.updateSidebar('user/approved'); + + var view = new ApprovedSiteListView({model:this.approvedSiteList, clientList: this.clientList, systemScopeList: this.systemScopeList}); + view.load( + function(collection, response, options) { + $('#content').html(view.render().el); + setPageTitle($.t('grant.manage-approved-sites')); + } + ); + } +}); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/profile.js b/openid-connect-server-webapp/src/main/webapp/resources/js/profile.js new file mode 100644 index 0000000000..4945ac711d --- /dev/null +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/profile.js @@ -0,0 +1,18 @@ +ui.routes.push({path: "user/profile", name: "profile", callback: + function() { + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('admin.user-profile.show'), href:"manage/#user/profile"} + ]); + + this.updateSidebar('user/profile'); + + var view = new UserProfileView({model: getUserInfo()}); + $('#content').html(view.render().el); + + setPageTitle($.t('admin.user-profile.show')); + + } +}); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js b/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js index 31ad139c81..4e17b7039a 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js @@ -426,4 +426,80 @@ var ResRegEditView = Backbone.View.extend({ return this; } -}); \ No newline at end of file +}); + +ui.routes.push({path: "dev/resource", name: "resReg", callback: + function() { + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('admin.self-service-resource'), href:"manage/#dev/resource"} + ]); + + this.updateSidebar('dev/resource'); + + var view = new ResRegRootView({systemScopeList: this.systemScopeList}); + view.load(function() { + $('#content').html(view.render().el); + + setPageTitle($.t('admin.self-service-resource')); + }); + + } +}); + +ui.routes.push({path: "dev/resource/new", name: "newResReg", callback: + function() { + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('admin.self-service-resource'), href:"manage/#dev/resource"}, + {text:$.t('rsreg.new'), href:"manage/#dev/resource/new"} + ]); + + this.updateSidebar('dev/resource'); + + var client = new ResRegClient(); + var view = new ResRegEditView({model: client, systemScopeList:this.systemScopeList}); + + view.load(function() { + + var userInfo = getUserInfo(); + var contacts = []; + if (userInfo != null && userInfo.email != null) { + contacts.push(userInfo.email); + } + + client.set({ + scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), + token_endpoint_auth_method: 'client_secret_basic', + contacts: contacts + }, { silent: true }); + + $('#content').html(view.render().el); + view.delegateEvents(); + setPageTitle($.t('rsreg.new')); + + }); + + } +}); + +ui.routes.push({path: "dev/resource/edit", name: "editResReg", callback: + function() { + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('admin.self-service-resource'), href:"manage/#dev/resource"}, + {text:$.t('rsreg.edit'), href:"manage/#dev/resource/edit"} + ]); + + this.updateSidebar('dev/resource'); + + setPageTitle($.t('rsreg.edit')); + // note that this doesn't actually load the client, that's supposed to happen elsewhere... + } +}); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js b/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js index 7de8a255cc..8e8e96366b 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js @@ -99,8 +99,8 @@ var SystemScopeView = Backbone.View.extend({ $('.restricted', this.el).tooltip({title: $.t('scope.system-scope-table.tooltip-restricted')}); $('.default', this.el).tooltip({title: $.t('scope.system-scope-table.tooltip-default')}); - return this; $(this.el).i18n(); + return this; }, deleteScope:function (e) { @@ -346,3 +346,90 @@ var SystemScopeFormView = Backbone.View.extend({ return this; } }); + +ui.routes.push({path: "admin/scope", name: "siteScope", callback: + function() { + + if (!isAdmin()) { + this.root(); + return; + } + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('scope.manage'), href:"manage/#admin/scope"} + ]); + + this.updateSidebar('admin/scope'); + + var view = new SystemScopeListView({model:this.systemScopeList}); + + view.load(function() { + $('#content').html(view.render().el); + view.delegateEvents(); + setPageTitle($.t('scope.manage')); + }); + + } +}); + +ui.routes.push({path: "admin/scope/new", name:"newScope", callback: + function() { + + + if (!isAdmin()) { + this.root(); + return; + } + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('scope.manage'), href:"manage/#admin/scope"}, + {text:$.t('scope.system-scope-form.new'), href:"manage/#admin/scope/new"} + ]); + + this.updateSidebar('admin/scope'); + + var scope = new SystemScopeModel(); + + var view = new SystemScopeFormView({model:scope}); + view.load(function() { + $('#content').html(view.render().el); + setPageTitle($.t('scope.system-scope-form.new')); + }); + + } +}); + +ui.routes.push({path: "admin/scope/:id", name: "editScope", callback: + function(sid) { + + if (!isAdmin()) { + this.root(); + return; + } + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('scope.manage'), href:"manage/#admin/scope"}, + {text:$.t('scope.system-scope-form.edit'), href:"manage/#admin/scope/" + sid} + ]); + + this.updateSidebar('admin/scope'); + + var scope = this.systemScopeList.get(sid); + if (!scope) { + scope = new SystemScopeModel({id: sid}); + } + + var view = new SystemScopeFormView({model:scope}); + view.load(function() { + $('#content').html(view.render().el); + setPageTitle($.t('scope.system-scope-form.new')); + }); + + } +}); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/token.js b/openid-connect-server-webapp/src/main/webapp/resources/js/token.js index da52fe010a..a25cc3770a 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/token.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/token.js @@ -477,3 +477,26 @@ var TokenListView = Backbone.View.extend({ return this; } }); + + +ui.routes.push({path: "user/tokens", name: "tokens", callback: + function() { + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('token.manage'), href:"manage/#user/tokens"} + ]); + + this.updateSidebar('user/tokens'); + + var view = new TokenListView({model: {access: this.accessTokensList, refresh: this.refreshTokensList}, clientList: this.clientList, systemScopeList: this.systemScopeList}); + + view.load( + function(collection, response, options) { + $('#content').html(view.render().el); + setPageTitle($.t('token.manage')); + } + ); + + } +}); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js b/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js index d50eb1e9c7..ad20c0b8aa 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js @@ -389,3 +389,112 @@ var WhiteListFormView = Backbone.View.extend({ }); + +ui.routes.push({path: "admin/whitelists", name: "whiteList", callback: + function () { + + if (!isAdmin()) { + this.root(); + return; + } + + this.updateSidebar('admin/whitelists'); + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('whitelist.manage'), href:"manage/#admin/whitelists"} + ]); + + var view = new WhiteListListView({model:this.whiteListList, clientList: this.clientList, systemScopeList: this.systemScopeList}); + + view.load( + function() { + $('#content').html(view.render().el); + view.delegateEvents(); + setPageTitle($.t('whitelist.manage')); + } + ); + + + } +}); + +ui.routes.push({path: "admin/whitelist/new/:cid", name: "newWhitelist", callback: + function(cid) { + + + if (!isAdmin()) { + this.root(); + return; + } + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('whitelist.manage'), href:"manage/#admin/whitelists"}, + {text:$.t('whitelist.new'), href:"manage/#admin/whitelist/new/" + cid} + ]); + + this.updateSidebar('admin/whitelists'); + + var whiteList = new WhiteListModel(); + + var client = this.clientList.get(cid); + if (!client) { + client = new ClientModel({id: cid}); + } + + var view = new WhiteListFormView({model: whiteList, client: client, systemScopeList: this.systemScopeList}); + + view.load( + function() { + + // set the scopes on the model now that everything's loaded + whiteList.set({allowedScopes: client.get('scope')}, {silent: true}); + + $('#content').html(view.render().el); + view.delegateEvents(); + setPageTitle($.t('whitelist.manage')); + } + ); + + } +}); + + +ui.routes.push({path: "admin/whitelist/:id", name: "editWhitelist", callback: + function(id) { + + if (!isAdmin()) { + this.root(); + return; + } + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('whitelist.manage'), href:"manage/#admin/whitelists"}, + {text:$.t('whitelist.edit'), href:"manage/#admin/whitelist/" + id} + ]); + + this.updateSidebar('admin/whitelists'); + + var whiteList = this.whiteListList.get(id); + if (!whiteList) { + whiteList = new WhiteListModel({id: id}); + } + + var view = new WhiteListFormView({model: whiteList, clientList: this.clientList, systemScopeList: this.systemScopeList}); + + view.load( + function() { + $('#content').html(view.render().el); + view.delegateEvents(); + setPageTitle($.t('whitelist.manage')); + } + ); + + } + +}); \ No newline at end of file From 503d6f57253561c86f7d207a6c4b931eb83f3eda Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 20 Feb 2017 13:20:21 -0500 Subject: [PATCH 126/269] load all templates from configured files --- .../src/main/webapp/WEB-INF/tags/footer.tag | 3 ++- .../src/main/webapp/resources/js/admin.js | 22 ++++++++----------- .../src/main/webapp/resources/js/blacklist.js | 2 ++ .../src/main/webapp/resources/js/client.js | 2 ++ .../src/main/webapp/resources/js/dynreg.js | 4 +++- .../src/main/webapp/resources/js/grant.js | 2 ++ .../src/main/webapp/resources/js/rsreg.js | 2 ++ .../src/main/webapp/resources/js/scope.js | 4 +++- .../src/main/webapp/resources/js/token.js | 4 +++- .../src/main/webapp/resources/js/whitelist.js | 4 +++- 10 files changed, 31 insertions(+), 18 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag index 3834c2c3de..724954171a 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag @@ -20,7 +20,7 @@ // set up a global variable for UI components to hang extensions off of var ui = { - templates: [], + templates: ["resources/template/admin.html"], routes: [] }; @@ -39,5 +39,6 @@ +
diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js b/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js index f57e884a2f..493e6c8801 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js @@ -474,22 +474,18 @@ var app = null; // main $(function () { - var _load = function (templates) { - $('body').append(templates); + var loader = function(source) { + return $.get(source, function (templates) { + console.log('Loading file: ' + source); + $('#templates').append(templates); + }); }; - + // load templates and append them to the body - $.when( - $.get('resources/template/admin.html', _load), - $.get('resources/template/client.html', _load), - $.get('resources/template/grant.html', _load), - $.get('resources/template/scope.html', _load), - $.get('resources/template/whitelist.html', _load), - $.get('resources/template/dynreg.html', _load), - $.get('resources/template/rsreg.html', _load), - $.get('resources/template/token.html', _load), - $.get('resources/template/blacklist.html', _load) + $.when.apply(null, + ui.templates.map(loader) ).done(function() { + console.log('done'); $.ajaxSetup({cache:false}); app = new AppRouter(); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js b/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js index df97d056b4..c031379d94 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js @@ -208,3 +208,5 @@ ui.routes.push({path: "admin/blacklist", name: "blackList", callback: } }); + +ui.templates.push('resources/template/blacklist.html'); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js index 7c6839d2b7..713e21f766 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js @@ -1322,3 +1322,5 @@ ui.routes.push({path: "admin/client/:id", name: "editClient", callback: } }); + +ui.templates.push('resources/template/client.html'); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js b/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js index e0bb23b7e0..b46983e46e 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js @@ -705,4 +705,6 @@ ui.routes.push({path: "dev/dynreg/edit", name: "editDynReg", callback: setPageTitle($.t('dynreg.edit-existing')); // note that this doesn't actually load the client, that's supposed to happen elsewhere... } -}); \ No newline at end of file +}); + +ui.templates.push('resources/template/dynreg.html'); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js b/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js index 303963b4da..bcf10701c9 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js @@ -278,3 +278,5 @@ ui.routes.push({path: "user/approved", name: "approvedSites", callback: ); } }); + +ui.templates.push('resources/template/grant.html'); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js b/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js index 4e17b7039a..3b01bacbb0 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js @@ -503,3 +503,5 @@ ui.routes.push({path: "dev/resource/edit", name: "editResReg", callback: // note that this doesn't actually load the client, that's supposed to happen elsewhere... } }); + +ui.templates.push('resources/template/rsreg.html'); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js b/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js index 8e8e96366b..ea904c62e6 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js @@ -432,4 +432,6 @@ ui.routes.push({path: "admin/scope/:id", name: "editScope", callback: }); } -}); \ No newline at end of file +}); + +ui.templates.push('resources/template/scope.html'); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/token.js b/openid-connect-server-webapp/src/main/webapp/resources/js/token.js index a25cc3770a..4ea20b55a3 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/token.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/token.js @@ -499,4 +499,6 @@ ui.routes.push({path: "user/tokens", name: "tokens", callback: ); } -}); \ No newline at end of file +}); + +ui.templates.push('resources/template/token.html'); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js b/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js index ad20c0b8aa..d75d06e4f0 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js @@ -497,4 +497,6 @@ ui.routes.push({path: "admin/whitelist/:id", name: "editWhitelist", callback: } -}); \ No newline at end of file +}); + +ui.templates.push('resources/template/whitelist.html'); \ No newline at end of file From f2173907ace2e72328250604913317d81c989574 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 20 Feb 2017 14:06:41 -0500 Subject: [PATCH 127/269] modularized app router initialization functions --- .../src/main/webapp/WEB-INF/tags/footer.tag | 5 +++-- .../src/main/webapp/resources/js/admin.js | 16 +++++++--------- .../src/main/webapp/resources/js/blacklist.js | 6 +++++- .../src/main/webapp/resources/js/client.js | 6 +++++- .../src/main/webapp/resources/js/dynreg.js | 2 +- .../src/main/webapp/resources/js/grant.js | 6 +++++- .../src/main/webapp/resources/js/rsreg.js | 2 +- .../src/main/webapp/resources/js/scope.js | 6 +++++- .../src/main/webapp/resources/js/token.js | 7 ++++++- .../src/main/webapp/resources/js/whitelist.js | 6 +++++- 10 files changed, 43 insertions(+), 19 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag index 724954171a..a7de8fb97b 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag @@ -20,8 +20,9 @@ // set up a global variable for UI components to hang extensions off of var ui = { - templates: ["resources/template/admin.html"], - routes: [] + templates: ["resources/template/admin.html"], // template files to load for UI + routes: [], // routes to add to the UI {path: URI to map to, name: unique name for internal use, callback: function to call when route is activated} + init: [] // functions to call after initialization is complete }; diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js b/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js index 493e6c8801..a329c1caa4 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js @@ -430,14 +430,7 @@ var AppRouter = Backbone.Router.extend({ initialize:function () { - this.clientList = new ClientCollection(); - this.whiteListList = new WhiteListCollection(); - this.blackListList = new BlackListCollection(); - this.approvedSiteList = new ApprovedSiteCollection(); - this.systemScopeList = new SystemScopeCollection(); this.clientStats = new StatsModel(); - this.accessTokensList = new AccessTokenCollection(); - this.refreshTokensList = new RefreshTokenCollection(); this.breadCrumbView = new BreadCrumbView({ collection:new Backbone.Collection() @@ -447,6 +440,12 @@ var AppRouter = Backbone.Router.extend({ this.errorHandlerView = new ErrorHandlerView(); + // call all the extra initialization functions + var app = this; + _.each(ui.init, function(fn) { + fn(app); + }); + }, notImplemented:function(){ @@ -482,8 +481,7 @@ $(function () { }; // load templates and append them to the body - $.when.apply(null, - ui.templates.map(loader) + $.when.apply(null, ui.templates.map(loader) ).done(function() { console.log('done'); $.ajaxSetup({cache:false}); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js b/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js index c031379d94..719a685172 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js @@ -209,4 +209,8 @@ ui.routes.push({path: "admin/blacklist", name: "blackList", callback: }); -ui.templates.push('resources/template/blacklist.html'); \ No newline at end of file +ui.templates.push('resources/template/blacklist.html'); + +ui.init.push(function(app) { + app.blackListList = new BlackListCollection(); +}); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js index 713e21f766..8272d59fb9 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js @@ -1323,4 +1323,8 @@ ui.routes.push({path: "admin/client/:id", name: "editClient", callback: } }); -ui.templates.push('resources/template/client.html'); \ No newline at end of file +ui.templates.push('resources/template/client.html'); + +ui.init.push(function(app) { + app.clientList = new ClientCollection(); +}); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js b/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js index b46983e46e..e3d29ac79e 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js @@ -707,4 +707,4 @@ ui.routes.push({path: "dev/dynreg/edit", name: "editDynReg", callback: } }); -ui.templates.push('resources/template/dynreg.html'); \ No newline at end of file +ui.templates.push('resources/template/dynreg.html'); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js b/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js index bcf10701c9..948e1c17aa 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js @@ -279,4 +279,8 @@ ui.routes.push({path: "user/approved", name: "approvedSites", callback: } }); -ui.templates.push('resources/template/grant.html'); \ No newline at end of file +ui.templates.push('resources/template/grant.html'); + +ui.init.push(function(app) { + app.approvedSiteList = new ApprovedSiteCollection(); +}); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js b/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js index 3b01bacbb0..3507c65acc 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js @@ -504,4 +504,4 @@ ui.routes.push({path: "dev/resource/edit", name: "editResReg", callback: } }); -ui.templates.push('resources/template/rsreg.html'); \ No newline at end of file +ui.templates.push('resources/template/rsreg.html'); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js b/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js index ea904c62e6..aced6de280 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js @@ -434,4 +434,8 @@ ui.routes.push({path: "admin/scope/:id", name: "editScope", callback: } }); -ui.templates.push('resources/template/scope.html'); \ No newline at end of file +ui.templates.push('resources/template/scope.html'); + +ui.init.push(function(app) { + app.systemScopeList = new SystemScopeCollection(); +}); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/token.js b/openid-connect-server-webapp/src/main/webapp/resources/js/token.js index 4ea20b55a3..ed012a620a 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/token.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/token.js @@ -501,4 +501,9 @@ ui.routes.push({path: "user/tokens", name: "tokens", callback: } }); -ui.templates.push('resources/template/token.html'); \ No newline at end of file +ui.templates.push('resources/template/token.html'); + +ui.init.push(function(app) { + app.accessTokensList = new AccessTokenCollection(); + app.refreshTokensList = new RefreshTokenCollection(); +}); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js b/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js index d75d06e4f0..8441bdebdd 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js @@ -499,4 +499,8 @@ ui.routes.push({path: "admin/whitelist/:id", name: "editWhitelist", callback: }); -ui.templates.push('resources/template/whitelist.html'); \ No newline at end of file +ui.templates.push('resources/template/whitelist.html'); + +ui.init.push(function(app) { + app.whiteListList = new WhiteListCollection(); +}); From 9592c784d184279d2c0edbf2cdbdde5b3d2d4a70 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 20 Feb 2017 14:07:01 -0500 Subject: [PATCH 128/269] modularized UMA server UI --- .../src/main/webapp/WEB-INF/tags/footer.tag | 39 - .../src/main/webapp/WEB-INF/ui-config.xml | 59 + .../src/main/webapp/resources/js/admin.js | 1240 ----------------- .../src/main/webapp/resources/js/policy.js | 140 +- 4 files changed, 198 insertions(+), 1280 deletions(-) delete mode 100644 uma-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag create mode 100644 uma-server-webapp/src/main/webapp/WEB-INF/ui-config.xml delete mode 100644 uma-server-webapp/src/main/webapp/resources/js/admin.js diff --git a/uma-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag b/uma-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag deleted file mode 100644 index a7ff9499e9..0000000000 --- a/uma-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag +++ /dev/null @@ -1,39 +0,0 @@ -<%@ attribute name="js" required="false"%> -<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="o" tagdir="/WEB-INF/tags"%> -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/uma-server-webapp/src/main/webapp/WEB-INF/ui-config.xml b/uma-server-webapp/src/main/webapp/WEB-INF/ui-config.xml new file mode 100644 index 0000000000..7a2a84631e --- /dev/null +++ b/uma-server-webapp/src/main/webapp/WEB-INF/ui-config.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + resources/js/client.js + resources/js/grant.js + resources/js/scope.js + resources/js/whitelist.js + resources/js/dynreg.js + resources/js/rsreg.js + resources/js/token.js + resources/js/blacklist.js + resources/js/profile.js + resources/js/policy.js + + + + + + + + + + + + diff --git a/uma-server-webapp/src/main/webapp/resources/js/admin.js b/uma-server-webapp/src/main/webapp/resources/js/admin.js deleted file mode 100644 index c21e3a1430..0000000000 --- a/uma-server-webapp/src/main/webapp/resources/js/admin.js +++ /dev/null @@ -1,1240 +0,0 @@ -/******************************************************************************* - * Copyright 2017 The MITRE Corporation - * and the MIT Internet Trust Consortium - * - * 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. - *******************************************************************************/ - -Backbone.Model.prototype.fetchIfNeeded = function(options) { - var _self = this; - if (!options) { - options = {}; - } - var success = options.success; - options.success = function(c, r) { - _self.isFetched = true; - if (success) { - success(c, r); - } - }; - if (!this.isFetched) { - return this.fetch(options); - } else { - return options.success(this, null); - } -}; - -Backbone.Collection.prototype.fetchIfNeeded = function(options) { - var _self = this; - if (!options) { - options = {}; - } - var success = options.success; - options.success = function(c, r) { - _self.isFetched = true; - if (success) { - success(c, r); - } - }; - if (!this.isFetched) { - return this.fetch(options); - } else { - return options.success(this, null); - } -}; - -var URIModel = Backbone.Model.extend({ - - validate: function(attrs){ - - var expression = /^(?:([a-z0-9+.-]+:\/\/)((?:(?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*)@)?((?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*)(:(?:\d*))?(\/(?:[a-z0-9-._~!$&'()*+,;=:@\/]|%[0-9A-F]{2})*)?|([a-z0-9+.-]+:)(\/?(?:[a-z0-9-._~!$&'()*+,;=:@]|%[0-9A-F]{2})+(?:[a-z0-9-._~!$&'()*+,;=:@\/]|%[0-9A-F]{2})*)?)(\?(?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*)?(#(?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*)?$/i; - var regex = new RegExp(expression); - - if (attrs.item == null || !attrs.item.match(regex)) { - return "Invalid URI"; - } - } - -}); - - -/* -* Backbone JS Reusable ListWidget -* Options -* { -* collection: Backbone JS Collection -* type: ('uri'|'default') -* autocomplete: ['item1','item2'] List of auto complete items -* } -* - */ -var ListWidgetChildView = Backbone.View.extend({ - - tagName: 'tr', - - events:{ - "click .btn-delete-list-item":'deleteItem', - "change .checkbox-list-item":'toggleCheckbox' - }, - - deleteItem:function (e) { - e.preventDefault(); - e.stopImmediatePropagation(); - //this.$el.tooltip('delete'); - - this.model.destroy({ - dataType: false, processData: false, - error:app.errorHandlerView.handleError() - }); - - }, - - toggleCheckbox:function(e) { - e.preventDefault(); - e.stopImmediatePropagation(); - if ($(e.target).is(':checked')) { - this.options.collection.add(this.model); - } else { - this.options.collection.remove(this.model); - } - - }, - - initialize:function (options) { - this.options = {toggle: false, checked: false}; - _.extend(this.options, options); - if (!this.template) { - this.template = _.template($('#tmpl-list-widget-child').html()); - } - }, - - render:function () { - - var data = {model: this.model.toJSON(), opt: this.options}; - - this.$el.html(this.template(data)); - - $('.item-full', this.el).hide(); - - if (this.model.get('item').length > 30) { - this.$el.tooltip({title:$.t('admin.list-widget.tooltip')}); - - var _self = this; - - $(this.el).click(function(event) { - event.preventDefault(); - $('.item-short', _self.el).hide(); - $('.item-full', _self.el).show(); - _self.$el.tooltip('destroy'); - }); - } - - - - $(this.el).i18n(); - return this; - } -}); - -var ListWidgetView = Backbone.View.extend({ - - tagName: "div", - - events:{ - "click .btn-add-list-item":"addItem", - "keypress":function (e) { - // trap the enter key - if (e.which == 13) { - e.preventDefault(); - this.addItem(e); - $("input", this.$el).focus(); - } - } - }, - - initialize:function (options) { - this.options = options; - - if (!this.template) { - this.template = _.template($('#tmpl-list-widget').html()); - } - - this.collection.bind('add', this.render, this); - this.collection.bind('remove', this.render, this); - }, - - addItem:function(e) { - e.preventDefault(); - - var input_value = $("input", this.el).val().trim(); - - if (input_value === ""){ - return; - } - - var model; - - if (this.options.type == 'uri') { - model = new URIModel({item:input_value}); - } else { - model = new Backbone.Model({item:input_value}); - model.validate = function(attrs) { - if(!attrs.item) { - return "value can't be null"; - } - }; - } - - // if it's valid and doesn't already exist - if (model.get("item") != null && this.collection.where({item: input_value}).length < 1) { - this.collection.add(model); - } else { - // else add a visual error indicator - $(".control-group", this.el).addClass('error'); - } - }, - - render:function (eventName) { - - this.$el.html(this.template({placeholder:this.options.placeholder, - helpBlockText:this.options.helpBlockText})); - - var _self = this; - - if (_.size(this.collection.models) == 0 && _.size(this.options.autocomplete) == 0) { - $("tbody", _self.el).html($('#tmpl-list-widget-child-empty').html()); - } else { - - // make a copy of our collection to work from - var values = this.collection.clone(); - - // look through our autocomplete values (if we have them) and render them all as checkboxes - if (this.options.autocomplete) { - _.each(this.options.autocomplete, function(option) { - var found = _.find(values.models, function(element) { - return element.get('item') == option; - }); - - var model = null; - var checked = false; - - if (found) { - // if we found the element, check the box - model = found; - checked = true; - // and remove it from the list of items to be rendered later - values.remove(found, {silent: true}); - } else { - model = new Backbone.Model({item:option}); - checked = false; - } - - var el = new ListWidgetChildView({model:model, toggle: true, checked: checked, collection: _self.collection}).render().el; - $("tbody", _self.el).append(el); - - }, this); - } - - - // now render everything not in the autocomplete list - _.each(values.models, function (model) { - var el = new ListWidgetChildView({model:model, collection: _self.collection}).render().el; - $("tbody", _self.el).append(el); - }, this); - } - - $(this.el).i18n(); - return this; - } - -}); - -var BreadCrumbView = Backbone.View.extend({ - - tagName: 'ul', - - initialize:function (options) { - this.options = options; - - if (!this.template) { - this.template = _.template($('#tmpl-breadcrumbs').html()); - } - - this.$el.addClass('breadcrumb'); - - this.collection.bind('add', this.render, this); - }, - - render:function () { - - this.$el.empty(); - var parent = this; - - // go through each of the breadcrumb models - _.each(this.collection.models, function (crumb, index) { - - // if it's the last index in the crumbs then render the link inactive - if (index == parent.collection.size() - 1) { - crumb.set({active:true}, {silent:true}); - } else { - crumb.set({active:false}, {silent:true}); - } - - this.$el.append(this.template(crumb.toJSON())); - }, this); - - $('#breadcrumbs').html(this.el); - $(this.el).i18n(); - } -}); - - -// Stats table - -var StatsModel = Backbone.Model.extend({ - url: "api/stats/byclientid" -}); - -// User Profile - -var UserProfileView = Backbone.View.extend({ - tagName: 'span', - - initialize:function(options) { - this.options = options; - if (!this.template) { - this.template = _.template($('#tmpl-user-profile-element').html()); - } - }, - - render:function() { - - $(this.el).html($('#tmpl-user-profile').html()); - - var t = this.template; - - _.each(this.model, function (value, key) { - if (key && value) { - - if (typeof(value) === 'object') { - - var el = this.el; - var k = key; - - _.each(value, function (value, key) { - $('dl', el).append( - t({key: key, value: value, category: k}) - ); - }); - } else if (typeof(value) === 'array') { - // TODO: handle array types - } else { - $('dl', this.el).append( - t({key: key, value: value}) - ); - } - } - }, this); - - $(this.el).i18n(); - return this; - } -}); - -// error handler -var ErrorHandlerView = Backbone.View.extend({ - - initialize:function(options) { - this.options = options; - if (!this.template) { - this.template = _.template($('#tmpl-error-box').html()); - } - if (!this.headerTemplate) { - this.headerTemplate = _.template($('#tmpl-error-header').html()); - } - }, - - reloadPage:function(event) { - event.preventDefault(); - window.location.reload(true); - }, - - handleError:function(message) { - - if (!message) { - message = {}; - } - - var _self = this; - - return function(model, response, options) { - - if (message.log) { - console.log(message.log); - } - - _self.showErrorMessage( - _self.headerTemplate({message: message, model: model, response: response, options: options}), - _self.template({message: message, model: model, response: response, options: options}) - ); - - $('#modalAlert .modal-body .page-reload').on('click', _self.reloadPage); - - } - }, - - showErrorMessage:function(header, message) { - // hide the sheet if it's visible - $('#loadingbox').sheet('hide'); - - $('#modalAlert').i18n(); - $('#modalAlert div.modal-header').html(header); - $('#modalAlert .modal-body').html(message); - - $('#modalAlert').modal({ - 'backdrop': 'static', - 'keyboard': true, - 'show': true - }); - - } -}); - - -// Router -var AppRouter = Backbone.Router.extend({ - - routes:{ - "admin/clients":"listClients", - "admin/client/new":"newClient", - "admin/client/:id":"editClient", - - "admin/whitelists":"whiteList", - "admin/whitelist/new/:cid":"newWhitelist", - "admin/whitelist/:id":"editWhitelist", - - "admin/blacklist":"blackList", - - "admin/scope":"siteScope", - "admin/scope/new":"newScope", - "admin/scope/:id":"editScope", - - "user/approved":"approvedSites", - "user/tokens":"tokens", - "user/profile":"profile", - - "user/policy":"policy", - "user/policy/:rsid":"editPolicies", - "user/policy/:rsid/new":"newPolicy", - "user/policy/:rsid/:pid":"editPolicy", - - "dev/dynreg":"dynReg", - "dev/dynreg/new":"newDynReg", - "dev/dynreg/edit":"editDynReg", - - "dev/resource":"resReg", - "dev/resource/new":"newResReg", - "dev/resource/edit":"editResReg", - - "": "root" - - }, - - root:function() { - if (isAdmin()) { - this.navigate('admin/clients', {trigger: true}); - } else { - this.navigate('user/approved', {trigger: true}); - } - }, - - initialize:function () { - - this.clientList = new ClientCollection(); - this.whiteListList = new WhiteListCollection(); - this.blackListList = new BlackListCollection(); - this.approvedSiteList = new ApprovedSiteCollection(); - this.systemScopeList = new SystemScopeCollection(); - this.clientStats = new StatsModel(); - this.accessTokensList = new AccessTokenCollection(); - this.refreshTokensList = new RefreshTokenCollection(); - this.resourceSetList = new ResourceSetCollection(); - - this.breadCrumbView = new BreadCrumbView({ - collection:new Backbone.Collection() - }); - - this.breadCrumbView.render(); - - this.errorHandlerView = new ErrorHandlerView(); - - var base = $('base').attr('href'); - $.getJSON(base + '.well-known/openid-configuration', function(data) { - app.serverConfiguration = data; - var baseUrl = $.url(app.serverConfiguration.issuer); - Backbone.history.start({pushState: true, root: baseUrl.attr('relative') + 'manage/'}); - }); - - }, - - listClients:function () { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('client.manage'), href:"manage/#admin/clients"} - ]); - - this.updateSidebar('admin/clients'); - - var view = new ClientListView({model:this.clientList, stats: this.clientStats, systemScopeList: this.systemScopeList, whiteListList: this.whiteListList}); - - view.load(function() { - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('client.manage')); - }); - - }, - - newClient:function() { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('client.manage'), href:"manage/#admin/clients"}, - {text:$.t('client.client-form.new'), href:""} - ]); - - this.updateSidebar('admin/clients'); - - var client = new ClientModel(); - - var view = new ClientFormView({model:client, systemScopeList: this.systemScopeList}); - view.load(function() { - // set up this new client to require a secret and have us autogenerate one - var userInfo = getUserInfo(); - var contacts = []; - if (userInfo != null && userInfo.email != null) { - contacts.push(userInfo.email); - } - - // use a different set of defaults based on heart mode flag - if (heartMode) { - client.set({ - tokenEndpointAuthMethod: "PRIVATE_KEY", - generateClientSecret:false, - displayClientSecret:false, - requireAuthTime:true, - defaultMaxAge:60000, - scope: _.uniq(_.flatten(app.systemScopeList.defaultScopes().pluck("value"))), - accessTokenValiditySeconds:3600, - refreshTokenValiditySeconds:24*3600, - idTokenValiditySeconds:300, - grantTypes: ["authorization_code"], - responseTypes: ["code"], - subjectType: "PUBLIC", - jwksType: "URI", - contacts: contacts - }, { silent: true }); - } else { - // set up this new client to require a secret and have us autogenerate one - client.set({ - tokenEndpointAuthMethod: "SECRET_BASIC", - generateClientSecret:true, - displayClientSecret:false, - requireAuthTime:true, - defaultMaxAge:60000, - scope: _.uniq(_.flatten(app.systemScopeList.defaultScopes().pluck("value"))), - accessTokenValiditySeconds:3600, - idTokenValiditySeconds:600, - grantTypes: ["authorization_code"], - responseTypes: ["code"], - subjectType: "PUBLIC", - jwksType: "URI", - contacts: contacts - }, { silent: true }); - } - - - $('#content').html(view.render().el); - setPageTitle($.t('client.client-form.new')); - }); - }, - - editClient:function(id) { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('client.manage'), href:"manage/#admin/clients"}, - {text:$.t('client.client-form.edit'), href:"manage/#admin/client/" + id} - ]); - - this.updateSidebar('admin/clients'); - - var client = this.clientList.get(id); - if (!client) { - client = new ClientModel({id:id}); - } - - var view = new ClientFormView({model:client, systemScopeList: app.systemScopeList}); - view.load(function() { - if ($.inArray("refresh_token", client.get("grantTypes")) != -1) { - client.set({ - allowRefresh: true - }, { silent: true }); - } - - if (client.get("jwks")) { - client.set({ - jwksType: "VAL" - }, { silent: true }); - } else { - client.set({ - jwksType: "URI" - }, { silent: true }); - } - - client.set({ - generateClientSecret:false, - displayClientSecret:false - }, { silent: true }); - - $('#content').html(view.render().el); - setPageTitle($.t('client.client-form.edit')); - }); - - }, - - whiteList:function () { - - if (!isAdmin()) { - this.root(); - return; - } - - this.updateSidebar('admin/whitelists'); - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('whitelist.manage'), href:"manage/#admin/whitelists"} - ]); - - var view = new WhiteListListView({model:this.whiteListList, clientList: this.clientList, systemScopeList: this.systemScopeList}); - - view.load( - function() { - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('whitelist.manage')); - } - ); - - - }, - - newWhitelist:function(cid) { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('whitelist.manage'), href:"manage/#admin/whitelists"}, - {text:$.t('whitelist.new'), href:"manage/#admin/whitelist/new/" + cid} - ]); - - this.updateSidebar('admin/whitelists'); - - var whiteList = new WhiteListModel(); - - var client = this.clientList.get(cid); - if (!client) { - client = new ClientModel({id: cid}); - } - - var view = new WhiteListFormView({model: whiteList, client: client, systemScopeList: this.systemScopeList}); - - view.load( - function() { - - // set the scopes on the model now that everything's loaded - whiteList.set({allowedScopes: client.get('scope')}, {silent: true}); - - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('whitelist.manage')); - } - ); - - }, - - editWhitelist:function(id) { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('whitelist.manage'), href:"manage/#admin/whitelists"}, - {text:$.t('whitelist.edit'), href:"manage/#admin/whitelist/" + id} - ]); - - this.updateSidebar('admin/whitelists'); - - var whiteList = this.whiteListList.get(id); - if (!whiteList) { - whiteList = new WhiteListModel({id: id}); - } - - var view = new WhiteListFormView({model: whiteList, clientList: this.clientList, systemScopeList: this.systemScopeList}); - - view.load( - function() { - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('whitelist.manage')); - } - ); - - }, - - approvedSites:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('grant.manage-approved-sites'), href:"manage/#user/approve"} - ]); - - this.updateSidebar('user/approved'); - - var view = new ApprovedSiteListView({model:this.approvedSiteList, clientList: this.clientList, systemScopeList: this.systemScopeList}); - view.load( - function(collection, response, options) { - $('#content').html(view.render().el); - setPageTitle($.t('grant.manage-approved-sites')); - } - ); - - }, - - tokens:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('token.manage'), href:"manage/#user/tokens"} - ]); - - this.updateSidebar('user/tokens'); - - var view = new TokenListView({model: {access: this.accessTokensList, refresh: this.refreshTokensList}, clientList: this.clientList, systemScopeList: this.systemScopeList}); - - view.load( - function(collection, response, options) { - $('#content').html(view.render().el); - setPageTitle($.t('token.manage')); - } - ); - - }, - - notImplemented:function(){ - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""} - ]); - - this.updateSidebar('none'); - - $('#content').html("

Not implemented yet.

"); - }, - - blackList:function() { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.manage-blacklist'), href:"manage/#admin/blacklist"} - ]); - - this.updateSidebar('admin/blacklist'); - - var view = new BlackListListView({collection: this.blackListList}); - - view.load( - function(collection, response, options) { - $('#content').html(view.render().el); - setPageTitle($.t('admin.manage-blacklist')); - } - ); - }, - - siteScope:function() { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('scope.manage'), href:"manage/#admin/scope"} - ]); - - this.updateSidebar('admin/scope'); - - var view = new SystemScopeListView({model:this.systemScopeList}); - - view.load(function() { - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('scope.manage')); - }); - - }, - - newScope:function() { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('scope.manage'), href:"manage/#admin/scope"}, - {text:$.t('scope.system-scope-form.new'), href:"manage/#admin/scope/new"} - ]); - - this.updateSidebar('admin/scope'); - - var scope = new SystemScopeModel(); - - var view = new SystemScopeFormView({model:scope}); - view.load(function() { - $('#content').html(view.render().el); - setPageTitle($.t('scope.system-scope-form.new')); - }); - - }, - - editScope:function(sid) { - - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('scope.manage'), href:"manage/#admin/scope"}, - {text:$.t('scope.system-scope-form.edit'), href:"manage/#admin/scope/" + sid} - ]); - - this.updateSidebar('admin/scope'); - - var scope = this.systemScopeList.get(sid); - if (!scope) { - scope = new SystemScopeModel({id: sid}); - } - - var view = new SystemScopeFormView({model:scope}); - view.load(function() { - $('#content').html(view.render().el); - setPageTitle($.t('scope.system-scope-form.new')); - }); - - }, - - dynReg:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-client'), href:"manage/#dev/dynreg"} - ]); - - var view = new DynRegRootView({systemScopeList: this.systemScopeList}); - - this.updateSidebar('dev/dynreg'); - - view.load(function() { - $('#content').html(view.render().el); - - setPageTitle($.t('admin.self-service-client')); - }); - - }, - - newDynReg:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-client'), href:"manage/#dev/dynreg"}, - {text:$.t('dynreg.new-client'), href:"manage/#dev/dynreg/new"} - ]); - - this.updateSidebar('dev/dynreg'); - - var client = new DynRegClient(); - var view = new DynRegEditView({model: client, systemScopeList:this.systemScopeList}); - - view.load(function() { - - var userInfo = getUserInfo(); - var contacts = []; - if (userInfo != null && userInfo.email != null) { - contacts.push(userInfo.email); - } - - if (heartMode) { - client.set({ - require_auth_time:true, - default_max_age:60000, - scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), - token_endpoint_auth_method: 'private_key_jwt', - grant_types: ["authorization_code"], - response_types: ["code"], - subject_type: "public", - contacts: contacts - }, { silent: true }); - } else { - client.set({ - require_auth_time:true, - default_max_age:60000, - scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), - token_endpoint_auth_method: 'client_secret_basic', - grant_types: ["authorization_code"], - response_types: ["code"], - subject_type: "public", - contacts: contacts - }, { silent: true }); - } - - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('dynreg.new-client')); - - }); - - }, - - editDynReg:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-client'), href:"manage/#dev/dynreg"}, - {text:$.t('dynreg.edit-existing'), href:"manage/#dev/dynreg/edit"} - ]); - - this.updateSidebar('dev/dynreg'); - - setPageTitle($.t('dynreg.edit-existing')); - // note that this doesn't actually load the client, that's supposed to happen elsewhere... - }, - - resReg:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-resource'), href:"manage/#dev/resource"} - ]); - - this.updateSidebar('dev/resource'); - - var view = new ResRegRootView({systemScopeList: this.systemScopeList}); - view.load(function() { - $('#content').html(view.render().el); - - setPageTitle($.t('admin.self-service-resource')); - }); - - }, - - newResReg:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-resource'), href:"manage/#dev/resource"}, - {text:$.t('rsreg.new'), href:"manage/#dev/resource/new"} - ]); - - this.updateSidebar('dev/resource'); - - var client = new ResRegClient(); - var view = new ResRegEditView({model: client, systemScopeList:this.systemScopeList}); - - view.load(function() { - - var userInfo = getUserInfo(); - var contacts = []; - if (userInfo != null && userInfo.email != null) { - contacts.push(userInfo.email); - } - - client.set({ - scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), - token_endpoint_auth_method: 'client_secret_basic', - contacts: contacts - }, { silent: true }); - - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('rsreg.new')); - - }); - - }, - - editResReg:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-resource'), href:"manage/#dev/resource"}, - {text:$.t('rsreg.edit'), href:"manage/#dev/resource/edit"} - ]); - - this.updateSidebar('dev/resource'); - - setPageTitle($.t('rsreg.edit')); - // note that this doesn't actually load the client, that's supposed to happen elsewhere... - }, - - profile:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.user-profile.show'), href:"manage/#user/profile"} - ]); - - this.updateSidebar('user/profile'); - - var view = new UserProfileView({model: getUserInfo()}); - $('#content').html(view.render().el); - - setPageTitle($.t('admin.user-profile.show')); - - }, - - policy:function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('policy.resource-sets'), href:"manage/#user/policy"} - ]); - - this.updateSidebar('user/policy'); - - var view = new ResourceSetListView({model: this.resourceSetList, clientList: this.clientList, systemScopeList: this.systemScopeList}); - - view.load(function() { - $('#content').html(view.render().el); - setPageTitle($.t('policy.resource-sets')); - }); - - }, - - editPolicies:function(rsid) { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('policy.resource-sets'), href:"manage/#user/policy"}, - {text:$.t('policy.edit-policies'), href:"manage/#user/policy/" + rsid} - ]); - - this.updateSidebar('user/policy'); - - var rs = this.resourceSetList.get(rsid); - var policies = null; - if (rs == null) { - // need to load it directly - policies = new PolicyCollection([], {rsid: rsid}); - rs = new ResourceSetModel({id: rsid}); - this.resourceSetList.add(rs); // it will be loaded below, don't need to load it again in the future - } else { - // the resource set is loaded, preload the claims - policies = new PolicyCollection(rs.get('policies'), {rsid: rsid}); - policies.isFetched = true; - } - - var view = new PolicyListView({model: policies, rs: rs, systemScopeList: this.systemScopeList}); - - view.load(function() { - $('#content').html(view.render().el); - setPageTitle($.t('policy.edit-policy')); - }); - - }, - - newPolicy:function(rsid) { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('policy.resource-sets'), href:"manage/#user/policy"}, - {text:$.t('policy.edit-policies'), href:"manage/#user/policy/" + rsid}, - {text:$.t('policy.new-policy'), href:"manage/#user/policy/" + rsid + "/new"} - ]); - - this.updateSidebar('user/policy'); - - var policy = policy = new PolicyModel({}, {rsid: rsid}); - - var rs = this.resourceSetList.get(rsid); - if (rs == null) { - // need to load it directly - rs = new ResourceSetModel({id: rsid}); - this.resourceSetList.add(rs); // it will be loaded below, don't need to load it again in the future - } - - var view = new PolicyFormView({model: policy, rs: rs, systemScopeList: this.systemScopeList}); - - view.load(function() { - $('#content').html(view.render().el); - setPageTitle($.t('policy.edit-policy')); - }); - }, - - editPolicy:function(rsid, pid) { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('policy.resource-sets'), href:"manage/#user/policy"}, - {text:$.t('policy.edit-policies'), href:"manage/#user/policy/" + rsid}, - {text:$.t('policy.edit-policy'), href:"manage/#user/policy/" + rsid + "/" + pid} - ]); - - this.updateSidebar('user/policy'); - - var rs = this.resourceSetList.get(rsid); - var policy = null; - if (rs == null) { - // need to load it directly - policy = new PolicyModel({id: pid}, {rsid: rsid}); - rs = new ResourceSetModel({id: rsid}); - this.resourceSetList.add(rs); // it will be loaded below, don't need to load it again in the future - } else { - // the resource set is loaded, preload the claims - _.each(rs.get('policies'), function(p) { - if (p.id == pid) { - policy = new PolicyModel(p, {rsid: rsid}); - policy.isFetched = true; - } - }); - if (policy == null) { - // need to load it directly - policy = new PolicyModel({id: pid}, {rsid: rsid}); - } - } - - var view = new PolicyFormView({model: policy, rs: rs, systemScopeList: this.systemScopeList}); - - view.load(function() { - $('#content').html(view.render().el); - setPageTitle($.t('policy.edit-policy')); - }); - - - }, - - updateSidebar:function(item) { - $('.sidebar-nav li.active').removeClass('active'); - - $('.sidebar-nav li a[href^="manage/#' + item + '"]').parent().addClass('active'); - } -}); - -// holds the global app. -// this gets init after the templates load -var app = null; - -// main -$(function () { - - var _load = function (templates) { - $('body').append(templates); - }; - - // load templates and append them to the body - $.when( - $.get('resources/template/admin.html', _load), - $.get('resources/template/client.html', _load), - $.get('resources/template/grant.html', _load), - $.get('resources/template/scope.html', _load), - $.get('resources/template/whitelist.html', _load), - $.get('resources/template/dynreg.html', _load), - $.get('resources/template/rsreg.html', _load), - $.get('resources/template/token.html', _load), - $.get('resources/template/blacklist.html', _load), - $.get('resources/template/policy.html', _load) - ).done(function() { - $.ajaxSetup({cache:false}); - app = new AppRouter(); - - app.on('route', function(name, args) { - // scroll to top of page on new route selection - $("html, body").animate({ scrollTop: 0 }, "slow"); - }); - - // grab all hashed URLs and send them through the app router instead - $(document).on('click', 'a[href^="manage/#"]', function(event) { - event.preventDefault(); - app.navigate(this.hash.slice(1), {trigger: true}); - }); - }); - - window.onerror = function ( message, filename, lineno, colno, error ){ - console.log(message); - //Display an alert with an error message - $('#modalAlert div.modal-header').html($.t('error.title')); - $('#modalAlert div.modal-body').html($.t('error.message') + message + '
' + [filename, lineno, colno, error]); - - $("#modalAlert").modal({ // wire up the actual modal functionality and show the dialog - "backdrop" : "static", - "keyboard" : true, - "show" : true // ensure the modal is shown immediately - }); - - } -}); - - diff --git a/uma-server-webapp/src/main/webapp/resources/js/policy.js b/uma-server-webapp/src/main/webapp/resources/js/policy.js index 466c1af7b6..f87a409fe0 100644 --- a/uma-server-webapp/src/main/webapp/resources/js/policy.js +++ b/uma-server-webapp/src/main/webapp/resources/js/policy.js @@ -646,4 +646,142 @@ var PolicyFormView = Backbone.View.extend({ return this; } -}); \ No newline at end of file +}); + + +ui.routes.push({path: "user/policy", name: "policy", callback: + function() { + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('policy.resource-sets'), href:"manage/#user/policy"} + ]); + + this.updateSidebar('user/policy'); + + var view = new ResourceSetListView({model: this.resourceSetList, clientList: this.clientList, systemScopeList: this.systemScopeList}); + + view.load(function() { + $('#content').html(view.render().el); + setPageTitle($.t('policy.resource-sets')); + }); + + } +}); + +ui.routes.push({path: "user/policy/:rsid", name: "editPolicies", callback: + function(rsid) { + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('policy.resource-sets'), href:"manage/#user/policy"}, + {text:$.t('policy.edit-policies'), href:"manage/#user/policy/" + rsid} + ]); + + this.updateSidebar('user/policy'); + + var rs = this.resourceSetList.get(rsid); + var policies = null; + if (rs == null) { + // need to load it directly + policies = new PolicyCollection([], {rsid: rsid}); + rs = new ResourceSetModel({id: rsid}); + this.resourceSetList.add(rs); // it will be loaded below, don't need to load it again in the future + } else { + // the resource set is loaded, preload the claims + policies = new PolicyCollection(rs.get('policies'), {rsid: rsid}); + policies.isFetched = true; + } + + var view = new PolicyListView({model: policies, rs: rs, systemScopeList: this.systemScopeList}); + + view.load(function() { + $('#content').html(view.render().el); + setPageTitle($.t('policy.edit-policy')); + }); + + } +}); + +ui.routes.push({path: "user/policy/:rsid/new", name: "newPolicy", callback: + function(rsid) { + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('policy.resource-sets'), href:"manage/#user/policy"}, + {text:$.t('policy.edit-policies'), href:"manage/#user/policy/" + rsid}, + {text:$.t('policy.new-policy'), href:"manage/#user/policy/" + rsid + "/new"} + ]); + + this.updateSidebar('user/policy'); + + var policy = policy = new PolicyModel({}, {rsid: rsid}); + + var rs = this.resourceSetList.get(rsid); + if (rs == null) { + // need to load it directly + rs = new ResourceSetModel({id: rsid}); + this.resourceSetList.add(rs); // it will be loaded below, don't need to load it again in the future + } + + var view = new PolicyFormView({model: policy, rs: rs, systemScopeList: this.systemScopeList}); + + view.load(function() { + $('#content').html(view.render().el); + setPageTitle($.t('policy.edit-policy')); + }); + } +}); + +ui.routes.push({path: "user/policy/:rsid/:pid", name: "editPolicy", callback: + function(rsid, pid) { + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([ + {text:$.t('admin.home'), href:""}, + {text:$.t('policy.resource-sets'), href:"manage/#user/policy"}, + {text:$.t('policy.edit-policies'), href:"manage/#user/policy/" + rsid}, + {text:$.t('policy.edit-policy'), href:"manage/#user/policy/" + rsid + "/" + pid} + ]); + + this.updateSidebar('user/policy'); + + var rs = this.resourceSetList.get(rsid); + var policy = null; + if (rs == null) { + // need to load it directly + policy = new PolicyModel({id: pid}, {rsid: rsid}); + rs = new ResourceSetModel({id: rsid}); + this.resourceSetList.add(rs); // it will be loaded below, don't need to load it again in the future + } else { + // the resource set is loaded, preload the claims + _.each(rs.get('policies'), function(p) { + if (p.id == pid) { + policy = new PolicyModel(p, {rsid: rsid}); + policy.isFetched = true; + } + }); + if (policy == null) { + // need to load it directly + policy = new PolicyModel({id: pid}, {rsid: rsid}); + } + } + + var view = new PolicyFormView({model: policy, rs: rs, systemScopeList: this.systemScopeList}); + + view.load(function() { + $('#content').html(view.render().el); + setPageTitle($.t('policy.edit-policy')); + }); + + + } +}); + +ui.templates.push('resources/template/policy.html'); + +ui.init.push(function(app) { + app.resourceSetList = new ResourceSetCollection(); +}); From 26c34c70debd841aac1b9c50b43953fb88f01a89 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 20 Feb 2017 14:07:10 -0500 Subject: [PATCH 129/269] happy new year! again! --- .../src/main/webapp/resources/js/locale/en/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json index 0722b93a25..658a299332 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json @@ -362,7 +362,7 @@ "empty": "There are no blacklisted URIs on this server.", "uri": "URI" }, - "copyright": "Powered by MITREid Connect {0} © 2016 The MITRE Corporation and MIT Internet Trust Consortium..", + "copyright": "Powered by MITREid Connect {0} © 2017 The MITRE Corporation and MIT Internet Trust Consortium..", "about": { "title": "About", "body": "\nThis OpenID Connect service is built from the MITREid Connect Open Source project, from \nThe MITRE Corporation and the MIT Internet Trust Consortium.\n

\n

\nMore information about the project can be found at \nMITREid Connect on GitHub. \nThere, you can submit bug reports, give feedback, or even contribute code patches for additional features you'd like to see." From 40b4dfa5fee5470cb34f9ce5e057091e9c05886f Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 20 Feb 2017 14:32:24 -0500 Subject: [PATCH 130/269] extracted nav menu from topbar tag --- .../src/main/webapp/WEB-INF/tags/navmenu.tag | 39 ++++++++++++++++ .../src/main/webapp/WEB-INF/tags/topbar.tag | 46 +++---------------- 2 files changed, 46 insertions(+), 39 deletions(-) create mode 100644 openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/navmenu.tag diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/navmenu.tag b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/navmenu.tag new file mode 100644 index 0000000000..78bfe15cb5 --- /dev/null +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/navmenu.tag @@ -0,0 +1,39 @@ +<%@attribute name="pageName"%> +<%@ tag language="java" pageEncoding="UTF-8"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> +<%@ taglib prefix="security" + uri="http://www.springframework.org/security/tags"%> + + + +

  • + + +
  • +
    + + + +
  • +
    + +
  • +
    +
    + + +
  • +
    + +
  • +
    +
    + + +
  • +
    + +
  • +
    +
    diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/topbar.tag b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/topbar.tag index 9a2b0f7827..1bce4a1c5f 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/topbar.tag +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/topbar.tag @@ -44,48 +44,16 @@
    +
    + +
    + +
    +
    +
    diff --git a/openid-connect-server-webapp/src/main/webapp/resources/template/dynreg.html b/openid-connect-server-webapp/src/main/webapp/resources/template/dynreg.html index 2524db5e37..ffed2638a8 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/template/dynreg.html +++ b/openid-connect-server-webapp/src/main/webapp/resources/template/dynreg.html @@ -512,6 +512,17 @@

    + +
    + +
    + +
    +
    diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/AbstractClientEntityView.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/AbstractClientEntityView.java index a51c683a77..5b3229c429 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/AbstractClientEntityView.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/AbstractClientEntityView.java @@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.mitre.oauth2.model.PKCEAlgorithm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; @@ -36,11 +37,15 @@ import com.google.gson.ExclusionStrategy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; +import com.nimbusds.jose.Algorithm; import com.nimbusds.jose.EncryptionMethod; import com.nimbusds.jose.JWEAlgorithm; import com.nimbusds.jose.JWSAlgorithm; @@ -118,6 +123,15 @@ public JsonElement serialize(JWT src, Type typeOfSrc, JsonSerializationContext c } }) + .registerTypeAdapter(PKCEAlgorithm.class, new JsonSerializer() { + public JsonPrimitive serialize(PKCEAlgorithm src, Type typeOfSrc, JsonSerializationContext context) { + if (src != null) { + return new JsonPrimitive(src.getName()); + } else { + return null; + } + } + }) .serializeNulls() .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") .create(); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ClientAPI.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ClientAPI.java index bee16fb74a..93accfe93e 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ClientAPI.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ClientAPI.java @@ -29,6 +29,7 @@ import org.mitre.oauth2.model.ClientDetailsEntity.AppType; import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; import org.mitre.oauth2.model.ClientDetailsEntity.SubjectType; +import org.mitre.oauth2.model.PKCEAlgorithm; import org.mitre.oauth2.service.ClientDetailsEntityService; import org.mitre.oauth2.web.AuthenticationUtilities; import org.mitre.openid.connect.exception.ValidationException; @@ -68,9 +69,6 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; import com.google.gson.JsonSyntaxException; import com.nimbusds.jose.Algorithm; import com.nimbusds.jose.EncryptionMethod; @@ -203,6 +201,15 @@ public JWT deserialize(JsonElement json, Type typeOfT, JsonDeserializationContex } } }) + .registerTypeAdapter(PKCEAlgorithm.class, new JsonDeserializer() { + public PKCEAlgorithm deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (json.isJsonPrimitive()) { + return PKCEAlgorithm.parse(json.getAsString()); + } else { + return null; + } + } + }) .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") .create(); From 00ecd3dd22a1863a2607b748546547666315252f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonard=20Bru=CC=88nings?= Date: Thu, 2 Mar 2017 19:34:40 +0100 Subject: [PATCH 133/269] Fix NPE if no claims are requested for the userinfo object This happens if clients only requests id_token claims, or just send an empty claims parameter. Change-Id: I8bd176ad271bda8a1e2f26b6221bd8e2d0a3ebfb --- .../openid/connect/view/UserInfoView.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java index 00c6ff9f53..7609bc0768 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java @@ -148,18 +148,8 @@ private JsonObject toJsonFromRequestObj(UserInfo ui, Set scope, JsonObje Set authorizedByClaims = new HashSet<>(); Set requestedByClaims = new HashSet<>(); - if (authorizedClaims != null) { - JsonObject userinfoAuthorized = authorizedClaims.getAsJsonObject().get("userinfo").getAsJsonObject(); - for (Entry entry : userinfoAuthorized.getAsJsonObject().entrySet()) { - authorizedByClaims.add(entry.getKey()); - } - } - if (requestedClaims != null) { - JsonObject userinfoRequested = requestedClaims.getAsJsonObject().get("userinfo").getAsJsonObject(); - for (Entry entry : userinfoRequested.getAsJsonObject().entrySet()) { - requestedByClaims.add(entry.getKey()); - } - } + extractUserInfoClaimsIntoSet(authorizedClaims, authorizedByClaims); + extractUserInfoClaimsIntoSet(requestedClaims, requestedByClaims); // Filter claims by performing a manual intersection of claims that are allowed by the given scope, requested, and authorized. // We cannot use Sets.intersection() or similar because Entry<> objects will evaluate to being unequal if their values are @@ -180,4 +170,15 @@ private JsonObject toJsonFromRequestObj(UserInfo ui, Set scope, JsonObje return result; } + + private void extractUserInfoClaimsIntoSet(JsonObject claims, Set target) { + if (claims != null) { + JsonObject userinfoAuthorized = claims.getAsJsonObject("userinfo"); + if (userinfoAuthorized != null) { + for (Entry entry : userinfoAuthorized.entrySet()) { + target.add(entry.getKey()); + } + } + } + } } From 98a4d56cddcd8b9a93d4cf640bc3a1c8a07633b7 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 3 Mar 2017 17:20:15 -0500 Subject: [PATCH 134/269] made extraction function less side-effect-ful --- .../mitre/openid/connect/view/UserInfoView.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java index 7609bc0768..438a247e66 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java @@ -145,11 +145,8 @@ private JsonObject toJsonFromRequestObj(UserInfo ui, Set scope, JsonObje JsonObject obj = ui.toJson(); Set allowedByScope = translator.getClaimsForScopeSet(scope); - Set authorizedByClaims = new HashSet<>(); - Set requestedByClaims = new HashSet<>(); - - extractUserInfoClaimsIntoSet(authorizedClaims, authorizedByClaims); - extractUserInfoClaimsIntoSet(requestedClaims, requestedByClaims); + Set authorizedByClaims = extractUserInfoClaimsIntoSet(authorizedClaims); + Set requestedByClaims = extractUserInfoClaimsIntoSet(requestedClaims); // Filter claims by performing a manual intersection of claims that are allowed by the given scope, requested, and authorized. // We cannot use Sets.intersection() or similar because Entry<> objects will evaluate to being unequal if their values are @@ -171,7 +168,13 @@ private JsonObject toJsonFromRequestObj(UserInfo ui, Set scope, JsonObje return result; } - private void extractUserInfoClaimsIntoSet(JsonObject claims, Set target) { + /** + * Pull the claims that have been targeted into a set for processing. + * Returns an empty set if the input is null. + * @param claims the claims request to process + */ + private Set extractUserInfoClaimsIntoSet(JsonObject claims) { + Set target = new HashSet<>(); if (claims != null) { JsonObject userinfoAuthorized = claims.getAsJsonObject("userinfo"); if (userinfoAuthorized != null) { @@ -180,5 +183,6 @@ private void extractUserInfoClaimsIntoSet(JsonObject claims, Set target) } } } + return target; } } From f056eb93871a847d639085e3da36619ce33a81bb Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 27 Feb 2017 15:27:48 -0500 Subject: [PATCH 135/269] compile bootstrap CSS from source --- openid-connect-server-webapp/pom.xml | 20 + .../src/main/less/accordion.less | 34 + .../src/main/less/alerts.less | 79 + .../src/main/less/bootstrap-responsive.less | 48 + .../src/main/less/bootstrap.less | 63 + .../src/main/less/breadcrumbs.less | 24 + .../src/main/less/button-groups.less | 229 + .../src/main/less/buttons.less | 228 + .../src/main/less/carousel.less | 158 + .../src/main/less/close.less | 32 + .../src/main/less/code.less | 61 + .../src/main/less/component-animations.less | 22 + .../src/main/less/dropdowns.less | 248 + .../src/main/less/forms.less | 690 ++ .../src/main/less/grid.less | 21 + .../src/main/less/hero-unit.less | 25 + .../src/main/less/labels-badges.less | 84 + .../src/main/less/layouts.less | 16 + .../src/main/less/media.less | 55 + .../src/main/less/mixins.less | 702 ++ .../src/main/less/modals.less | 95 + .../src/main/less/navbar.less | 497 ++ .../src/main/less/navs.less | 409 ++ .../src/main/less/pager.less | 43 + .../src/main/less/pagination.less | 123 + .../src/main/less/popovers.less | 133 + .../src/main/less/progress-bars.less | 122 + .../src/main/less/reset.less | 216 + .../src/main/less/responsive-1200px-min.less | 28 + .../src/main/less/responsive-767px-max.less | 193 + .../src/main/less/responsive-768px-979px.less | 19 + .../src/main/less/responsive-navbar.less | 189 + .../src/main/less/responsive-utilities.less | 59 + .../src/main/less/scaffolding.less | 53 + .../src/main/less/sprites.less | 197 + .../src/main/less/tables.less | 244 + .../src/main/less/thumbnails.less | 53 + .../src/main/less/tooltip.less | 70 + .../src/main/less/type.less | 247 + .../src/main/less/utilities.less | 30 + .../src/main/less/variables.less | 301 + .../src/main/less/wells.less | 29 + .../bootstrap2/css/bootstrap-responsive.css | 1109 --- .../css/bootstrap-responsive.min.css | 9 - .../resources/bootstrap2/css/bootstrap.css | 6167 ----------------- .../bootstrap2/css/bootstrap.min.css | 9 - pom.xml | 12 + 47 files changed, 6201 insertions(+), 7294 deletions(-) create mode 100644 openid-connect-server-webapp/src/main/less/accordion.less create mode 100644 openid-connect-server-webapp/src/main/less/alerts.less create mode 100644 openid-connect-server-webapp/src/main/less/bootstrap-responsive.less create mode 100644 openid-connect-server-webapp/src/main/less/bootstrap.less create mode 100644 openid-connect-server-webapp/src/main/less/breadcrumbs.less create mode 100644 openid-connect-server-webapp/src/main/less/button-groups.less create mode 100644 openid-connect-server-webapp/src/main/less/buttons.less create mode 100644 openid-connect-server-webapp/src/main/less/carousel.less create mode 100644 openid-connect-server-webapp/src/main/less/close.less create mode 100644 openid-connect-server-webapp/src/main/less/code.less create mode 100644 openid-connect-server-webapp/src/main/less/component-animations.less create mode 100644 openid-connect-server-webapp/src/main/less/dropdowns.less create mode 100644 openid-connect-server-webapp/src/main/less/forms.less create mode 100644 openid-connect-server-webapp/src/main/less/grid.less create mode 100644 openid-connect-server-webapp/src/main/less/hero-unit.less create mode 100644 openid-connect-server-webapp/src/main/less/labels-badges.less create mode 100644 openid-connect-server-webapp/src/main/less/layouts.less create mode 100644 openid-connect-server-webapp/src/main/less/media.less create mode 100644 openid-connect-server-webapp/src/main/less/mixins.less create mode 100644 openid-connect-server-webapp/src/main/less/modals.less create mode 100644 openid-connect-server-webapp/src/main/less/navbar.less create mode 100644 openid-connect-server-webapp/src/main/less/navs.less create mode 100644 openid-connect-server-webapp/src/main/less/pager.less create mode 100644 openid-connect-server-webapp/src/main/less/pagination.less create mode 100644 openid-connect-server-webapp/src/main/less/popovers.less create mode 100644 openid-connect-server-webapp/src/main/less/progress-bars.less create mode 100644 openid-connect-server-webapp/src/main/less/reset.less create mode 100644 openid-connect-server-webapp/src/main/less/responsive-1200px-min.less create mode 100644 openid-connect-server-webapp/src/main/less/responsive-767px-max.less create mode 100644 openid-connect-server-webapp/src/main/less/responsive-768px-979px.less create mode 100644 openid-connect-server-webapp/src/main/less/responsive-navbar.less create mode 100644 openid-connect-server-webapp/src/main/less/responsive-utilities.less create mode 100644 openid-connect-server-webapp/src/main/less/scaffolding.less create mode 100644 openid-connect-server-webapp/src/main/less/sprites.less create mode 100644 openid-connect-server-webapp/src/main/less/tables.less create mode 100644 openid-connect-server-webapp/src/main/less/thumbnails.less create mode 100644 openid-connect-server-webapp/src/main/less/tooltip.less create mode 100644 openid-connect-server-webapp/src/main/less/type.less create mode 100644 openid-connect-server-webapp/src/main/less/utilities.less create mode 100644 openid-connect-server-webapp/src/main/less/variables.less create mode 100644 openid-connect-server-webapp/src/main/less/wells.less delete mode 100644 openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.css delete mode 100644 openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.min.css delete mode 100644 openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.css delete mode 100644 openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.min.css diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index e2b1f15746..77a95afb67 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -83,6 +83,26 @@ + + org.lesscss + lesscss-maven-plugin + + ${project.basedir}/src/main/less + ${project.build.directory}/${project.build.finalName}/resources/bootstrap2/css + false + + bootstrap.less + bootstrap-responsive.less + + + + + + compile + + + + diff --git a/openid-connect-server-webapp/src/main/less/accordion.less b/openid-connect-server-webapp/src/main/less/accordion.less new file mode 100644 index 0000000000..d63523bc8c --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/accordion.less @@ -0,0 +1,34 @@ +// +// Accordion +// -------------------------------------------------- + + +// Parent container +.accordion { + margin-bottom: @baseLineHeight; +} + +// Group == heading + body +.accordion-group { + margin-bottom: 2px; + border: 1px solid #e5e5e5; + .border-radius(@baseBorderRadius); +} +.accordion-heading { + border-bottom: 0; +} +.accordion-heading .accordion-toggle { + display: block; + padding: 8px 15px; +} + +// General toggle styles +.accordion-toggle { + cursor: pointer; +} + +// Inner needs the styles because you can't animate properly with any styles on the element +.accordion-inner { + padding: 9px 15px; + border-top: 1px solid #e5e5e5; +} diff --git a/openid-connect-server-webapp/src/main/less/alerts.less b/openid-connect-server-webapp/src/main/less/alerts.less new file mode 100644 index 0000000000..0116b191b3 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/alerts.less @@ -0,0 +1,79 @@ +// +// Alerts +// -------------------------------------------------- + + +// Base styles +// ------------------------- + +.alert { + padding: 8px 35px 8px 14px; + margin-bottom: @baseLineHeight; + text-shadow: 0 1px 0 rgba(255,255,255,.5); + background-color: @warningBackground; + border: 1px solid @warningBorder; + .border-radius(@baseBorderRadius); +} +.alert, +.alert h4 { + // Specified for the h4 to prevent conflicts of changing @headingsColor + color: @warningText; +} +.alert h4 { + margin: 0; +} + +// Adjust close link position +.alert .close { + position: relative; + top: -2px; + right: -21px; + line-height: @baseLineHeight; +} + + +// Alternate styles +// ------------------------- + +.alert-success { + background-color: @successBackground; + border-color: @successBorder; + color: @successText; +} +.alert-success h4 { + color: @successText; +} +.alert-danger, +.alert-error { + background-color: @errorBackground; + border-color: @errorBorder; + color: @errorText; +} +.alert-danger h4, +.alert-error h4 { + color: @errorText; +} +.alert-info { + background-color: @infoBackground; + border-color: @infoBorder; + color: @infoText; +} +.alert-info h4 { + color: @infoText; +} + + +// Block alerts +// ------------------------- + +.alert-block { + padding-top: 14px; + padding-bottom: 14px; +} +.alert-block > p, +.alert-block > ul { + margin-bottom: 0; +} +.alert-block p + p { + margin-top: 5px; +} diff --git a/openid-connect-server-webapp/src/main/less/bootstrap-responsive.less b/openid-connect-server-webapp/src/main/less/bootstrap-responsive.less new file mode 100644 index 0000000000..3d4c58cabd --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/bootstrap-responsive.less @@ -0,0 +1,48 @@ +/*! + * Bootstrap Responsive v2.3.2 + * + * Copyright 2013 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world by @mdo and @fat. + */ + + +// Responsive.less +// For phone and tablet devices +// ------------------------------------------------------------- + + +// REPEAT VARIABLES & MIXINS +// ------------------------- +// Required since we compile the responsive stuff separately + +@import "variables.less"; // Modify this for custom colors, font-sizes, etc +@import "mixins.less"; + + +// RESPONSIVE CLASSES +// ------------------ + +@import "responsive-utilities.less"; + + +// MEDIA QUERIES +// ------------------ + +// Large desktops +@import "responsive-1200px-min.less"; + +// Tablets to regular desktops +@import "responsive-768px-979px.less"; + +// Phones to portrait tablets and narrow desktops +@import "responsive-767px-max.less"; + + +// RESPONSIVE NAVBAR +// ------------------ + +// From 979px and below, show a button to toggle navbar contents +@import "responsive-navbar.less"; diff --git a/openid-connect-server-webapp/src/main/less/bootstrap.less b/openid-connect-server-webapp/src/main/less/bootstrap.less new file mode 100644 index 0000000000..3eabae1440 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/bootstrap.less @@ -0,0 +1,63 @@ +/*! + * Bootstrap v2.3.2 + * + * Copyright 2013 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world by @mdo and @fat. + */ + +// Core variables and mixins +@import "variables.less"; // Modify this for custom colors, font-sizes, etc +@import "mixins.less"; + +// CSS Reset +@import "reset.less"; + +// Grid system and page structure +@import "scaffolding.less"; +@import "grid.less"; +@import "layouts.less"; + +// Base CSS +@import "type.less"; +@import "code.less"; +@import "forms.less"; +@import "tables.less"; + +// Components: common +@import "sprites.less"; +@import "dropdowns.less"; +@import "wells.less"; +@import "component-animations.less"; +@import "close.less"; + +// Components: Buttons & Alerts +@import "buttons.less"; +@import "button-groups.less"; +@import "alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less + +// Components: Nav +@import "navs.less"; +@import "navbar.less"; +@import "breadcrumbs.less"; +@import "pagination.less"; +@import "pager.less"; + +// Components: Popovers +@import "modals.less"; +@import "tooltip.less"; +@import "popovers.less"; + +// Components: Misc +@import "thumbnails.less"; +@import "media.less"; +@import "labels-badges.less"; +@import "progress-bars.less"; +@import "accordion.less"; +@import "carousel.less"; +@import "hero-unit.less"; + +// Utility classes +@import "utilities.less"; // Has to be last to override when necessary diff --git a/openid-connect-server-webapp/src/main/less/breadcrumbs.less b/openid-connect-server-webapp/src/main/less/breadcrumbs.less new file mode 100644 index 0000000000..f753df6be8 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/breadcrumbs.less @@ -0,0 +1,24 @@ +// +// Breadcrumbs +// -------------------------------------------------- + + +.breadcrumb { + padding: 8px 15px; + margin: 0 0 @baseLineHeight; + list-style: none; + background-color: #f5f5f5; + .border-radius(@baseBorderRadius); + > li { + display: inline-block; + .ie7-inline-block(); + text-shadow: 0 1px 0 @white; + > .divider { + padding: 0 5px; + color: #ccc; + } + } + > .active { + color: @grayLight; + } +} diff --git a/openid-connect-server-webapp/src/main/less/button-groups.less b/openid-connect-server-webapp/src/main/less/button-groups.less new file mode 100644 index 0000000000..55cdc60338 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/button-groups.less @@ -0,0 +1,229 @@ +// +// Button groups +// -------------------------------------------------- + + +// Make the div behave like a button +.btn-group { + position: relative; + display: inline-block; + .ie7-inline-block(); + font-size: 0; // remove as part 1 of font-size inline-block hack + vertical-align: middle; // match .btn alignment given font-size hack above + white-space: nowrap; // prevent buttons from wrapping when in tight spaces (e.g., the table on the tests page) + .ie7-restore-left-whitespace(); +} + +// Space out series of button groups +.btn-group + .btn-group { + margin-left: 5px; +} + +// Optional: Group multiple button groups together for a toolbar +.btn-toolbar { + font-size: 0; // Hack to remove whitespace that results from using inline-block + margin-top: @baseLineHeight / 2; + margin-bottom: @baseLineHeight / 2; + > .btn + .btn, + > .btn-group + .btn, + > .btn + .btn-group { + margin-left: 5px; + } +} + +// Float them, remove border radius, then re-add to first and last elements +.btn-group > .btn { + position: relative; + .border-radius(0); +} +.btn-group > .btn + .btn { + margin-left: -1px; +} +.btn-group > .btn, +.btn-group > .dropdown-menu, +.btn-group > .popover { + font-size: @baseFontSize; // redeclare as part 2 of font-size inline-block hack +} + +// Reset fonts for other sizes +.btn-group > .btn-mini { + font-size: @fontSizeMini; +} +.btn-group > .btn-small { + font-size: @fontSizeSmall; +} +.btn-group > .btn-large { + font-size: @fontSizeLarge; +} + +// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match +.btn-group > .btn:first-child { + margin-left: 0; + .border-top-left-radius(@baseBorderRadius); + .border-bottom-left-radius(@baseBorderRadius); +} +// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it +.btn-group > .btn:last-child, +.btn-group > .dropdown-toggle { + .border-top-right-radius(@baseBorderRadius); + .border-bottom-right-radius(@baseBorderRadius); +} +// Reset corners for large buttons +.btn-group > .btn.large:first-child { + margin-left: 0; + .border-top-left-radius(@borderRadiusLarge); + .border-bottom-left-radius(@borderRadiusLarge); +} +.btn-group > .btn.large:last-child, +.btn-group > .large.dropdown-toggle { + .border-top-right-radius(@borderRadiusLarge); + .border-bottom-right-radius(@borderRadiusLarge); +} + +// On hover/focus/active, bring the proper btn to front +.btn-group > .btn:hover, +.btn-group > .btn:focus, +.btn-group > .btn:active, +.btn-group > .btn.active { + z-index: 2; +} + +// On active and open, don't show outline +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} + + + +// Split button dropdowns +// ---------------------- + +// Give the line between buttons some depth +.btn-group > .btn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; + .box-shadow(~"inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); + *padding-top: 5px; + *padding-bottom: 5px; +} +.btn-group > .btn-mini + .dropdown-toggle { + padding-left: 5px; + padding-right: 5px; + *padding-top: 2px; + *padding-bottom: 2px; +} +.btn-group > .btn-small + .dropdown-toggle { + *padding-top: 5px; + *padding-bottom: 4px; +} +.btn-group > .btn-large + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; + *padding-top: 7px; + *padding-bottom: 7px; +} + +.btn-group.open { + + // The clickable button for toggling the menu + // Remove the gradient and set the same inset shadow as the :active state + .dropdown-toggle { + background-image: none; + .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); + } + + // Keep the hover's background when dropdown is open + .btn.dropdown-toggle { + background-color: @btnBackgroundHighlight; + } + .btn-primary.dropdown-toggle { + background-color: @btnPrimaryBackgroundHighlight; + } + .btn-warning.dropdown-toggle { + background-color: @btnWarningBackgroundHighlight; + } + .btn-danger.dropdown-toggle { + background-color: @btnDangerBackgroundHighlight; + } + .btn-success.dropdown-toggle { + background-color: @btnSuccessBackgroundHighlight; + } + .btn-info.dropdown-toggle { + background-color: @btnInfoBackgroundHighlight; + } + .btn-inverse.dropdown-toggle { + background-color: @btnInverseBackgroundHighlight; + } +} + + +// Reposition the caret +.btn .caret { + margin-top: 8px; + margin-left: 0; +} +// Carets in other button sizes +.btn-large .caret { + margin-top: 6px; +} +.btn-large .caret { + border-left-width: 5px; + border-right-width: 5px; + border-top-width: 5px; +} +.btn-mini .caret, +.btn-small .caret { + margin-top: 8px; +} +// Upside down carets for .dropup +.dropup .btn-large .caret { + border-bottom-width: 5px; +} + + + +// Account for other colors +.btn-primary, +.btn-warning, +.btn-danger, +.btn-info, +.btn-success, +.btn-inverse { + .caret { + border-top-color: @white; + border-bottom-color: @white; + } +} + + + +// Vertical button groups +// ---------------------- + +.btn-group-vertical { + display: inline-block; // makes buttons only take up the width they need + .ie7-inline-block(); +} +.btn-group-vertical > .btn { + display: block; + float: none; + max-width: 100%; + .border-radius(0); +} +.btn-group-vertical > .btn + .btn { + margin-left: 0; + margin-top: -1px; +} +.btn-group-vertical > .btn:first-child { + .border-radius(@baseBorderRadius @baseBorderRadius 0 0); +} +.btn-group-vertical > .btn:last-child { + .border-radius(0 0 @baseBorderRadius @baseBorderRadius); +} +.btn-group-vertical > .btn-large:first-child { + .border-radius(@borderRadiusLarge @borderRadiusLarge 0 0); +} +.btn-group-vertical > .btn-large:last-child { + .border-radius(0 0 @borderRadiusLarge @borderRadiusLarge); +} diff --git a/openid-connect-server-webapp/src/main/less/buttons.less b/openid-connect-server-webapp/src/main/less/buttons.less new file mode 100644 index 0000000000..4cd4d862b3 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/buttons.less @@ -0,0 +1,228 @@ +// +// Buttons +// -------------------------------------------------- + + +// Base styles +// -------------------------------------------------- + +// Core +.btn { + display: inline-block; + .ie7-inline-block(); + padding: 4px 12px; + margin-bottom: 0; // For input.btn + font-size: @baseFontSize; + line-height: @baseLineHeight; + text-align: center; + vertical-align: middle; + cursor: pointer; + .buttonBackground(@btnBackground, @btnBackgroundHighlight, @grayDark, 0 1px 1px rgba(255,255,255,.75)); + border: 1px solid @btnBorder; + *border: 0; // Remove the border to prevent IE7's black border on input:focus + border-bottom-color: darken(@btnBorder, 10%); + .border-radius(@baseBorderRadius); + .ie7-restore-left-whitespace(); // Give IE7 some love + .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); + + // Hover/focus state + &:hover, + &:focus { + color: @grayDark; + text-decoration: none; + background-position: 0 -15px; + + // transition is only when going to hover/focus, otherwise the background + // behind the gradient (there for IE<=9 fallback) gets mismatched + .transition(background-position .1s linear); + } + + // Focus state for keyboard and accessibility + &:focus { + .tab-focus(); + } + + // Active state + &.active, + &:active { + background-image: none; + outline: 0; + .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); + } + + // Disabled state + &.disabled, + &[disabled] { + cursor: default; + background-image: none; + .opacity(65); + .box-shadow(none); + } + +} + + + +// Button Sizes +// -------------------------------------------------- + +// Large +.btn-large { + padding: @paddingLarge; + font-size: @fontSizeLarge; + .border-radius(@borderRadiusLarge); +} +.btn-large [class^="icon-"], +.btn-large [class*=" icon-"] { + margin-top: 4px; +} + +// Small +.btn-small { + padding: @paddingSmall; + font-size: @fontSizeSmall; + .border-radius(@borderRadiusSmall); +} +.btn-small [class^="icon-"], +.btn-small [class*=" icon-"] { + margin-top: 0; +} +.btn-mini [class^="icon-"], +.btn-mini [class*=" icon-"] { + margin-top: -1px; +} + +// Mini +.btn-mini { + padding: @paddingMini; + font-size: @fontSizeMini; + .border-radius(@borderRadiusSmall); +} + + +// Block button +// ------------------------- + +.btn-block { + display: block; + width: 100%; + padding-left: 0; + padding-right: 0; + .box-sizing(border-box); +} + +// Vertically space out multiple block buttons +.btn-block + .btn-block { + margin-top: 5px; +} + +// Specificity overrides +input[type="submit"], +input[type="reset"], +input[type="button"] { + &.btn-block { + width: 100%; + } +} + + + +// Alternate buttons +// -------------------------------------------------- + +// Provide *some* extra contrast for those who can get it +.btn-primary.active, +.btn-warning.active, +.btn-danger.active, +.btn-success.active, +.btn-info.active, +.btn-inverse.active { + color: rgba(255,255,255,.75); +} + +// Set the backgrounds +// ------------------------- +.btn-primary { + .buttonBackground(@btnPrimaryBackground, @btnPrimaryBackgroundHighlight); +} +// Warning appears are orange +.btn-warning { + .buttonBackground(@btnWarningBackground, @btnWarningBackgroundHighlight); +} +// Danger and error appear as red +.btn-danger { + .buttonBackground(@btnDangerBackground, @btnDangerBackgroundHighlight); +} +// Success appears as green +.btn-success { + .buttonBackground(@btnSuccessBackground, @btnSuccessBackgroundHighlight); +} +// Info appears as a neutral blue +.btn-info { + .buttonBackground(@btnInfoBackground, @btnInfoBackgroundHighlight); +} +// Inverse appears as dark gray +.btn-inverse { + .buttonBackground(@btnInverseBackground, @btnInverseBackgroundHighlight); +} + + +// Cross-browser Jank +// -------------------------------------------------- + +button.btn, +input[type="submit"].btn { + + // Firefox 3.6 only I believe + &::-moz-focus-inner { + padding: 0; + border: 0; + } + + // IE7 has some default padding on button controls + *padding-top: 3px; + *padding-bottom: 3px; + + &.btn-large { + *padding-top: 7px; + *padding-bottom: 7px; + } + &.btn-small { + *padding-top: 3px; + *padding-bottom: 3px; + } + &.btn-mini { + *padding-top: 1px; + *padding-bottom: 1px; + } +} + + +// Link buttons +// -------------------------------------------------- + +// Make a button look and behave like a link +.btn-link, +.btn-link:active, +.btn-link[disabled] { + background-color: transparent; + background-image: none; + .box-shadow(none); +} +.btn-link { + border-color: transparent; + cursor: pointer; + color: @linkColor; + .border-radius(0); +} +.btn-link:hover, +.btn-link:focus { + color: @linkColorHover; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +.btn-link[disabled]:focus { + color: @grayDark; + text-decoration: none; +} diff --git a/openid-connect-server-webapp/src/main/less/carousel.less b/openid-connect-server-webapp/src/main/less/carousel.less new file mode 100644 index 0000000000..55bc050144 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/carousel.less @@ -0,0 +1,158 @@ +// +// Carousel +// -------------------------------------------------- + + +.carousel { + position: relative; + margin-bottom: @baseLineHeight; + line-height: 1; +} + +.carousel-inner { + overflow: hidden; + width: 100%; + position: relative; +} + +.carousel-inner { + + > .item { + display: none; + position: relative; + .transition(.6s ease-in-out left); + + // Account for jankitude on images + > img, + > a > img { + display: block; + line-height: 1; + } + } + + > .active, + > .next, + > .prev { display: block; } + + > .active { + left: 0; + } + + > .next, + > .prev { + position: absolute; + top: 0; + width: 100%; + } + + > .next { + left: 100%; + } + > .prev { + left: -100%; + } + > .next.left, + > .prev.right { + left: 0; + } + + > .active.left { + left: -100%; + } + > .active.right { + left: 100%; + } + +} + +// Left/right controls for nav +// --------------------------- + +.carousel-control { + position: absolute; + top: 40%; + left: 15px; + width: 40px; + height: 40px; + margin-top: -20px; + font-size: 60px; + font-weight: 100; + line-height: 30px; + color: @white; + text-align: center; + background: @grayDarker; + border: 3px solid @white; + .border-radius(23px); + .opacity(50); + + // we can't have this transition here + // because webkit cancels the carousel + // animation if you trip this while + // in the middle of another animation + // ;_; + // .transition(opacity .2s linear); + + // Reposition the right one + &.right { + left: auto; + right: 15px; + } + + // Hover/focus state + &:hover, + &:focus { + color: @white; + text-decoration: none; + .opacity(90); + } +} + +// Carousel indicator pips +// ----------------------------- +.carousel-indicators { + position: absolute; + top: 15px; + right: 15px; + z-index: 5; + margin: 0; + list-style: none; + + li { + display: block; + float: left; + width: 10px; + height: 10px; + margin-left: 5px; + text-indent: -999px; + background-color: #ccc; + background-color: rgba(255,255,255,.25); + border-radius: 5px; + } + .active { + background-color: #fff; + } +} + +// Caption for text below images +// ----------------------------- + +.carousel-caption { + position: absolute; + left: 0; + right: 0; + bottom: 0; + padding: 15px; + background: @grayDark; + background: rgba(0,0,0,.75); +} +.carousel-caption h4, +.carousel-caption p { + color: @white; + line-height: @baseLineHeight; +} +.carousel-caption h4 { + margin: 0 0 5px; +} +.carousel-caption p { + margin-bottom: 0; +} diff --git a/openid-connect-server-webapp/src/main/less/close.less b/openid-connect-server-webapp/src/main/less/close.less new file mode 100644 index 0000000000..4c626bda6c --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/close.less @@ -0,0 +1,32 @@ +// +// Close icons +// -------------------------------------------------- + + +.close { + float: right; + font-size: 20px; + font-weight: bold; + line-height: @baseLineHeight; + color: @black; + text-shadow: 0 1px 0 rgba(255,255,255,1); + .opacity(20); + &:hover, + &:focus { + color: @black; + text-decoration: none; + cursor: pointer; + .opacity(40); + } +} + +// Additional properties for button version +// iOS requires the button element instead of an anchor tag. +// If you want the anchor version, it requires `href="#"`. +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/less/code.less b/openid-connect-server-webapp/src/main/less/code.less new file mode 100644 index 0000000000..266a926e73 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/code.less @@ -0,0 +1,61 @@ +// +// Code (inline and blocK) +// -------------------------------------------------- + + +// Inline and block code styles +code, +pre { + padding: 0 3px 2px; + #font > #family > .monospace; + font-size: @baseFontSize - 2; + color: @grayDark; + .border-radius(3px); +} + +// Inline code +code { + padding: 2px 4px; + color: #d14; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; + white-space: nowrap; +} + +// Blocks of code +pre { + display: block; + padding: (@baseLineHeight - 1) / 2; + margin: 0 0 @baseLineHeight / 2; + font-size: @baseFontSize - 1; // 14px to 13px + line-height: @baseLineHeight; + word-break: break-all; + word-wrap: break-word; + white-space: pre; + white-space: pre-wrap; + background-color: #f5f5f5; + border: 1px solid #ccc; // fallback for IE7-8 + border: 1px solid rgba(0,0,0,.15); + .border-radius(@baseBorderRadius); + + // Make prettyprint styles more spaced out for readability + &.prettyprint { + margin-bottom: @baseLineHeight; + } + + // Account for some code outputs that place code tags in pre tags + code { + padding: 0; + color: inherit; + white-space: pre; + white-space: pre-wrap; + background-color: transparent; + border: 0; + } +} + +// Enable scrollable blocks of code +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/less/component-animations.less b/openid-connect-server-webapp/src/main/less/component-animations.less new file mode 100644 index 0000000000..d614263a76 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/component-animations.less @@ -0,0 +1,22 @@ +// +// Component animations +// -------------------------------------------------- + + +.fade { + opacity: 0; + .transition(opacity .15s linear); + &.in { + opacity: 1; + } +} + +.collapse { + position: relative; + height: 0; + overflow: hidden; + .transition(height .35s ease); + &.in { + height: auto; + } +} diff --git a/openid-connect-server-webapp/src/main/less/dropdowns.less b/openid-connect-server-webapp/src/main/less/dropdowns.less new file mode 100644 index 0000000000..9e47b47151 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/dropdowns.less @@ -0,0 +1,248 @@ +// +// Dropdown menus +// -------------------------------------------------- + + +// Use the .menu class on any
  • element within the topbar or ul.tabs and you'll get some superfancy dropdowns +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle { + // The caret makes the toggle a bit too tall in IE7 + *margin-bottom: -3px; +} +.dropdown-toggle:active, +.open .dropdown-toggle { + outline: 0; +} + +// Dropdown arrow/caret +// -------------------- +.caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: top; + border-top: 4px solid @black; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + content: ""; +} + +// Place the caret +.dropdown .caret { + margin-top: 8px; + margin-left: 2px; +} + +// The dropdown menu (ul) +// ---------------------- +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: @zindexDropdown; + display: none; // none by default, but block on "open" of the menu + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; // override default ul + list-style: none; + background-color: @dropdownBackground; + border: 1px solid #ccc; // Fallback for IE7-8 + border: 1px solid @dropdownBorder; + *border-right-width: 2px; + *border-bottom-width: 2px; + .border-radius(6px); + .box-shadow(0 5px 10px rgba(0,0,0,.2)); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + + // Aligns the dropdown menu to right + &.pull-right { + right: 0; + left: auto; + } + + // Dividers (basically an hr) within the dropdown + .divider { + .nav-divider(@dropdownDividerTop, @dropdownDividerBottom); + } + + // Links within the dropdown menu + > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: @baseLineHeight; + color: @dropdownLinkColor; + white-space: nowrap; + } +} + +// Hover/Focus state +// ----------- +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus, +.dropdown-submenu:hover > a, +.dropdown-submenu:focus > a { + text-decoration: none; + color: @dropdownLinkColorHover; + #gradient > .vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%)); +} + +// Active state +// ------------ +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: @dropdownLinkColorActive; + text-decoration: none; + outline: 0; + #gradient > .vertical(@dropdownLinkBackgroundActive, darken(@dropdownLinkBackgroundActive, 5%)); +} + +// Disabled state +// -------------- +// Gray out text and ensure the hover/focus state remains gray +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: @grayLight; +} +// Nuke hover/focus effects +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + background-image: none; // Remove CSS gradient + .reset-filter(); + cursor: default; +} + +// Open state for the dropdown +// --------------------------- +.open { + // IE7's z-index only goes to the nearest positioned ancestor, which would + // make the menu appear below buttons that appeared later on the page + *z-index: @zindexDropdown; + + & > .dropdown-menu { + display: block; + } +} + +// Backdrop to catch body clicks on mobile, etc. +// --------------------------- +.dropdown-backdrop { + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: 0; + z-index: @zindexDropdown - 10; +} + +// Right aligned dropdowns +// --------------------------- +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} + +// Allow for dropdowns to go bottom up (aka, dropup-menu) +// ------------------------------------------------------ +// Just add .dropup after the standard .dropdown class and you're set, bro. +// TODO: abstract this so that the navbar fixed styles are not placed here? +.dropup, +.navbar-fixed-bottom .dropdown { + // Reverse the caret + .caret { + border-top: 0; + border-bottom: 4px solid @black; + content: ""; + } + // Different positioning for bottom up menu + .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; + } +} + +// Sub menus +// --------------------------- +.dropdown-submenu { + position: relative; +} +// Default dropdowns +.dropdown-submenu > .dropdown-menu { + top: 0; + left: 100%; + margin-top: -6px; + margin-left: -1px; + .border-radius(0 6px 6px 6px); +} +.dropdown-submenu:hover > .dropdown-menu { + display: block; +} + +// Dropups +.dropup .dropdown-submenu > .dropdown-menu { + top: auto; + bottom: 0; + margin-top: 0; + margin-bottom: -2px; + .border-radius(5px 5px 5px 0); +} + +// Caret to indicate there is a submenu +.dropdown-submenu > a:after { + display: block; + content: " "; + float: right; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + border-width: 5px 0 5px 5px; + border-left-color: darken(@dropdownBackground, 20%); + margin-top: 5px; + margin-right: -10px; +} +.dropdown-submenu:hover > a:after { + border-left-color: @dropdownLinkColorHover; +} + +// Left aligned submenus +.dropdown-submenu.pull-left { + // Undo the float + // Yes, this is awkward since .pull-left adds a float, but it sticks to our conventions elsewhere. + float: none; + + // Positioning the submenu + > .dropdown-menu { + left: -100%; + margin-left: 10px; + .border-radius(6px 0 6px 6px); + } +} + +// Tweak nav headers +// ----------------- +// Increase padding from 15px to 20px on sides +.dropdown .dropdown-menu .nav-header { + padding-left: 20px; + padding-right: 20px; +} + +// Typeahead +// --------- +.typeahead { + z-index: 1051; + margin-top: 2px; // give it some space to breathe + .border-radius(@baseBorderRadius); +} diff --git a/openid-connect-server-webapp/src/main/less/forms.less b/openid-connect-server-webapp/src/main/less/forms.less new file mode 100644 index 0000000000..06767bdd3e --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/forms.less @@ -0,0 +1,690 @@ +// +// Forms +// -------------------------------------------------- + + +// GENERAL STYLES +// -------------- + +// Make all forms have space below them +form { + margin: 0 0 @baseLineHeight; +} + +fieldset { + padding: 0; + margin: 0; + border: 0; +} + +// Groups of fields with labels on top (legends) +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: @baseLineHeight; + font-size: @baseFontSize * 1.5; + line-height: @baseLineHeight * 2; + color: @grayDark; + border: 0; + border-bottom: 1px solid #e5e5e5; + + // Small + small { + font-size: @baseLineHeight * .75; + color: @grayLight; + } +} + +// Set font for forms +label, +input, +button, +select, +textarea { + #font > .shorthand(@baseFontSize,normal,@baseLineHeight); // Set size, weight, line-height here +} +input, +button, +select, +textarea { + font-family: @baseFontFamily; // And only set font-family here for those that need it (note the missing label element) +} + +// Identify controls by their labels +label { + display: block; + margin-bottom: 5px; +} + +// Form controls +// ------------------------- + +// Shared size and type resets +select, +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + display: inline-block; + height: @baseLineHeight; + padding: 4px 6px; + margin-bottom: @baseLineHeight / 2; + font-size: @baseFontSize; + line-height: @baseLineHeight; + color: @gray; + .border-radius(@inputBorderRadius); + vertical-align: middle; +} + +// Reset appearance properties for textual inputs and textarea +// Declare width for legacy (can't be on input[type=*] selectors or it's too specific) +input, +textarea, +.uneditable-input { + width: 206px; // plus 12px padding and 2px border +} +// Reset height since textareas have rows +textarea { + height: auto; +} +// Everything else +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + background-color: @inputBackground; + border: 1px solid @inputBorder; + .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); + .transition(~"border linear .2s, box-shadow linear .2s"); + + // Focus state + &:focus { + border-color: rgba(82,168,236,.8); + outline: 0; + outline: thin dotted \9; /* IE6-9 */ + .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6)"); + } +} + +// Position radios and checkboxes better +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + *margin-top: 0; /* IE7 */ + margin-top: 1px \9; /* IE8-9 */ + line-height: normal; +} + +// Reset width of input images, buttons, radios, checkboxes +input[type="file"], +input[type="image"], +input[type="submit"], +input[type="reset"], +input[type="button"], +input[type="radio"], +input[type="checkbox"] { + width: auto; // Override of generic input selector +} + +// Set the height of select and file controls to match text inputs +select, +input[type="file"] { + height: @inputHeight; /* In IE7, the height of the select element cannot be changed by height, only font-size */ + *margin-top: 4px; /* For IE7, add top margin to align select with labels */ + line-height: @inputHeight; +} + +// Make select elements obey height by applying a border +select { + width: 220px; // default input width + 10px of padding that doesn't get applied + border: 1px solid @inputBorder; + background-color: @inputBackground; // Chrome on Linux and Mobile Safari need background-color +} + +// Make multiple select elements height not fixed +select[multiple], +select[size] { + height: auto; +} + +// Focus for select, file, radio, and checkbox +select:focus, +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + .tab-focus(); +} + + +// Uneditable inputs +// ------------------------- + +// Make uneditable inputs look inactive +.uneditable-input, +.uneditable-textarea { + color: @grayLight; + background-color: darken(@inputBackground, 1%); + border-color: @inputBorder; + .box-shadow(inset 0 1px 2px rgba(0,0,0,.025)); + cursor: not-allowed; +} + +// For text that needs to appear as an input but should not be an input +.uneditable-input { + overflow: hidden; // prevent text from wrapping, but still cut it off like an input does + white-space: nowrap; +} + +// Make uneditable textareas behave like a textarea +.uneditable-textarea { + width: auto; + height: auto; +} + + +// Placeholder +// ------------------------- + +// Placeholder text gets special styles because when browsers invalidate entire lines if it doesn't understand a selector +input, +textarea { + .placeholder(); +} + + +// CHECKBOXES & RADIOS +// ------------------- + +// Indent the labels to position radios/checkboxes as hanging +.radio, +.checkbox { + min-height: @baseLineHeight; // clear the floating input if there is no label text + padding-left: 20px; +} +.radio input[type="radio"], +.checkbox input[type="checkbox"] { + float: left; + margin-left: -20px; +} + +// Move the options list down to align with labels +.controls > .radio:first-child, +.controls > .checkbox:first-child { + padding-top: 5px; // has to be padding because margin collaspes +} + +// Radios and checkboxes on same line +// TODO v3: Convert .inline to .control-inline +.radio.inline, +.checkbox.inline { + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} +.radio.inline + .radio.inline, +.checkbox.inline + .checkbox.inline { + margin-left: 10px; // space out consecutive inline controls +} + + + +// INPUT SIZES +// ----------- + +// General classes for quick sizes +.input-mini { width: 60px; } +.input-small { width: 90px; } +.input-medium { width: 150px; } +.input-large { width: 210px; } +.input-xlarge { width: 270px; } +.input-xxlarge { width: 530px; } + +// Grid style input sizes +input[class*="span"], +select[class*="span"], +textarea[class*="span"], +.uneditable-input[class*="span"], +// Redeclare since the fluid row class is more specific +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"] { + float: none; + margin-left: 0; +} +// Ensure input-prepend/append never wraps +.input-append input[class*="span"], +.input-append .uneditable-input[class*="span"], +.input-prepend input[class*="span"], +.input-prepend .uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"], +.row-fluid .input-prepend [class*="span"], +.row-fluid .input-append [class*="span"] { + display: inline-block; +} + + + +// GRID SIZING FOR INPUTS +// ---------------------- + +// Grid sizes +#grid > .input(@gridColumnWidth, @gridGutterWidth); + +// Control row for multiple inputs per line +.controls-row { + .clearfix(); // Clear the float from controls +} + +// Float to collapse white-space for proper grid alignment +.controls-row [class*="span"], +// Redeclare the fluid grid collapse since we undo the float for inputs +.row-fluid .controls-row [class*="span"] { + float: left; +} +// Explicity set top padding on all checkboxes/radios, not just first-child +.controls-row .checkbox[class*="span"], +.controls-row .radio[class*="span"] { + padding-top: 5px; +} + + + + +// DISABLED STATE +// -------------- + +// Disabled and read-only inputs +input[disabled], +select[disabled], +textarea[disabled], +input[readonly], +select[readonly], +textarea[readonly] { + cursor: not-allowed; + background-color: @inputDisabledBackground; +} +// Explicitly reset the colors here +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"][readonly], +input[type="checkbox"][readonly] { + background-color: transparent; +} + + + + +// FORM FIELD FEEDBACK STATES +// -------------------------- + +// Warning +.control-group.warning { + .formFieldState(@warningText, @warningText, @warningBackground); +} +// Error +.control-group.error { + .formFieldState(@errorText, @errorText, @errorBackground); +} +// Success +.control-group.success { + .formFieldState(@successText, @successText, @successBackground); +} +// Success +.control-group.info { + .formFieldState(@infoText, @infoText, @infoBackground); +} + +// HTML5 invalid states +// Shares styles with the .control-group.error above +input:focus:invalid, +textarea:focus:invalid, +select:focus:invalid { + color: #b94a48; + border-color: #ee5f5b; + &:focus { + border-color: darken(#ee5f5b, 10%); + @shadow: 0 0 6px lighten(#ee5f5b, 20%); + .box-shadow(@shadow); + } +} + + + +// FORM ACTIONS +// ------------ + +.form-actions { + padding: (@baseLineHeight - 1) 20px @baseLineHeight; + margin-top: @baseLineHeight; + margin-bottom: @baseLineHeight; + background-color: @formActionsBackground; + border-top: 1px solid #e5e5e5; + .clearfix(); // Adding clearfix to allow for .pull-right button containers +} + + + +// HELP TEXT +// --------- + +.help-block, +.help-inline { + color: lighten(@textColor, 15%); // lighten the text some for contrast +} + +.help-block { + display: block; // account for any element using help-block + margin-bottom: @baseLineHeight / 2; +} + +.help-inline { + display: inline-block; + .ie7-inline-block(); + vertical-align: middle; + padding-left: 5px; +} + + + +// INPUT GROUPS +// ------------ + +// Allow us to put symbols and text within the input field for a cleaner look +.input-append, +.input-prepend { + display: inline-block; + margin-bottom: @baseLineHeight / 2; + vertical-align: middle; + font-size: 0; // white space collapse hack + white-space: nowrap; // Prevent span and input from separating + + // Reset the white space collapse hack + input, + select, + .uneditable-input, + .dropdown-menu, + .popover { + font-size: @baseFontSize; + } + + input, + select, + .uneditable-input { + position: relative; // placed here by default so that on :focus we can place the input above the .add-on for full border and box-shadow goodness + margin-bottom: 0; // prevent bottom margin from screwing up alignment in stacked forms + *margin-left: 0; + vertical-align: top; + .border-radius(0 @inputBorderRadius @inputBorderRadius 0); + // Make input on top when focused so blue border and shadow always show + &:focus { + z-index: 2; + } + } + .add-on { + display: inline-block; + width: auto; + height: @baseLineHeight; + min-width: 16px; + padding: 4px 5px; + font-size: @baseFontSize; + font-weight: normal; + line-height: @baseLineHeight; + text-align: center; + text-shadow: 0 1px 0 @white; + background-color: @grayLighter; + border: 1px solid #ccc; + } + .add-on, + .btn, + .btn-group > .dropdown-toggle { + vertical-align: top; + .border-radius(0); + } + .active { + background-color: lighten(@green, 30); + border-color: @green; + } +} + +.input-prepend { + .add-on, + .btn { + margin-right: -1px; + } + .add-on:first-child, + .btn:first-child { + // FYI, `.btn:first-child` accounts for a button group that's prepended + .border-radius(@inputBorderRadius 0 0 @inputBorderRadius); + } +} + +.input-append { + input, + select, + .uneditable-input { + .border-radius(@inputBorderRadius 0 0 @inputBorderRadius); + + .btn-group .btn:last-child { + .border-radius(0 @inputBorderRadius @inputBorderRadius 0); + } + } + .add-on, + .btn, + .btn-group { + margin-left: -1px; + } + .add-on:last-child, + .btn:last-child, + .btn-group:last-child > .dropdown-toggle { + .border-radius(0 @inputBorderRadius @inputBorderRadius 0); + } +} + +// Remove all border-radius for inputs with both prepend and append +.input-prepend.input-append { + input, + select, + .uneditable-input { + .border-radius(0); + + .btn-group .btn { + .border-radius(0 @inputBorderRadius @inputBorderRadius 0); + } + } + .add-on:first-child, + .btn:first-child { + margin-right: -1px; + .border-radius(@inputBorderRadius 0 0 @inputBorderRadius); + } + .add-on:last-child, + .btn:last-child { + margin-left: -1px; + .border-radius(0 @inputBorderRadius @inputBorderRadius 0); + } + .btn-group:first-child { + margin-left: 0; + } +} + + + + +// SEARCH FORM +// ----------- + +input.search-query { + padding-right: 14px; + padding-right: 4px \9; + padding-left: 14px; + padding-left: 4px \9; /* IE7-8 doesn't have border-radius, so don't indent the padding */ + margin-bottom: 0; // Remove the default margin on all inputs + .border-radius(15px); +} + +/* Allow for input prepend/append in search forms */ +.form-search .input-append .search-query, +.form-search .input-prepend .search-query { + .border-radius(0); // Override due to specificity +} +.form-search .input-append .search-query { + .border-radius(14px 0 0 14px); +} +.form-search .input-append .btn { + .border-radius(0 14px 14px 0); +} +.form-search .input-prepend .search-query { + .border-radius(0 14px 14px 0); +} +.form-search .input-prepend .btn { + .border-radius(14px 0 0 14px); +} + + + + +// HORIZONTAL & VERTICAL FORMS +// --------------------------- + +// Common properties +// ----------------- + +.form-search, +.form-inline, +.form-horizontal { + input, + textarea, + select, + .help-inline, + .uneditable-input, + .input-prepend, + .input-append { + display: inline-block; + .ie7-inline-block(); + margin-bottom: 0; + vertical-align: middle; + } + // Re-hide hidden elements due to specifity + .hide { + display: none; + } +} +.form-search label, +.form-inline label, +.form-search .btn-group, +.form-inline .btn-group { + display: inline-block; +} +// Remove margin for input-prepend/-append +.form-search .input-append, +.form-inline .input-append, +.form-search .input-prepend, +.form-inline .input-prepend { + margin-bottom: 0; +} +// Inline checkbox/radio labels (remove padding on left) +.form-search .radio, +.form-search .checkbox, +.form-inline .radio, +.form-inline .checkbox { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} +// Remove float and margin, set to inline-block +.form-search .radio input[type="radio"], +.form-search .checkbox input[type="checkbox"], +.form-inline .radio input[type="radio"], +.form-inline .checkbox input[type="checkbox"] { + float: left; + margin-right: 3px; + margin-left: 0; +} + + +// Margin to space out fieldsets +.control-group { + margin-bottom: @baseLineHeight / 2; +} + +// Legend collapses margin, so next element is responsible for spacing +legend + .control-group { + margin-top: @baseLineHeight; + -webkit-margin-top-collapse: separate; +} + +// Horizontal-specific styles +// -------------------------- + +.form-horizontal { + // Increase spacing between groups + .control-group { + margin-bottom: @baseLineHeight; + .clearfix(); + } + // Float the labels left + .control-label { + float: left; + width: @horizontalComponentOffset - 20; + padding-top: 5px; + text-align: right; + } + // Move over all input controls and content + .controls { + // Super jank IE7 fix to ensure the inputs in .input-append and input-prepend + // don't inherit the margin of the parent, in this case .controls + *display: inline-block; + *padding-left: 20px; + margin-left: @horizontalComponentOffset; + *margin-left: 0; + &:first-child { + *padding-left: @horizontalComponentOffset; + } + } + // Remove bottom margin on block level help text since that's accounted for on .control-group + .help-block { + margin-bottom: 0; + } + // And apply it only to .help-block instances that follow a form control + input, + select, + textarea, + .uneditable-input, + .input-prepend, + .input-append { + + .help-block { + margin-top: @baseLineHeight / 2; + } + } + // Move over buttons in .form-actions to align with .controls + .form-actions { + padding-left: @horizontalComponentOffset; + } +} diff --git a/openid-connect-server-webapp/src/main/less/grid.less b/openid-connect-server-webapp/src/main/less/grid.less new file mode 100644 index 0000000000..750d203514 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/grid.less @@ -0,0 +1,21 @@ +// +// Grid system +// -------------------------------------------------- + + +// Fixed (940px) +#grid > .core(@gridColumnWidth, @gridGutterWidth); + +// Fluid (940px) +#grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth); + +// Reset utility classes due to specificity +[class*="span"].hide, +.row-fluid [class*="span"].hide { + display: none; +} + +[class*="span"].pull-right, +.row-fluid [class*="span"].pull-right { + float: right; +} diff --git a/openid-connect-server-webapp/src/main/less/hero-unit.less b/openid-connect-server-webapp/src/main/less/hero-unit.less new file mode 100644 index 0000000000..763d86aeee --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/hero-unit.less @@ -0,0 +1,25 @@ +// +// Hero unit +// -------------------------------------------------- + + +.hero-unit { + padding: 60px; + margin-bottom: 30px; + font-size: 18px; + font-weight: 200; + line-height: @baseLineHeight * 1.5; + color: @heroUnitLeadColor; + background-color: @heroUnitBackground; + .border-radius(6px); + h1 { + margin-bottom: 0; + font-size: 60px; + line-height: 1; + color: @heroUnitHeadingColor; + letter-spacing: -1px; + } + li { + line-height: @baseLineHeight * 1.5; // Reset since we specify in type.less + } +} diff --git a/openid-connect-server-webapp/src/main/less/labels-badges.less b/openid-connect-server-webapp/src/main/less/labels-badges.less new file mode 100644 index 0000000000..bc321fe5c1 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/labels-badges.less @@ -0,0 +1,84 @@ +// +// Labels and badges +// -------------------------------------------------- + + +// Base classes +.label, +.badge { + display: inline-block; + padding: 2px 4px; + font-size: @baseFontSize * .846; + font-weight: bold; + line-height: 14px; // ensure proper line-height if floated + color: @white; + vertical-align: baseline; + white-space: nowrap; + text-shadow: 0 -1px 0 rgba(0,0,0,.25); + background-color: @grayLight; +} +// Set unique padding and border-radii +.label { + .border-radius(3px); +} +.badge { + padding-left: 9px; + padding-right: 9px; + .border-radius(9px); +} + +// Empty labels/badges collapse +.label, +.badge { + &:empty { + display: none; + } +} + +// Hover/focus state, but only for links +a { + &.label:hover, + &.label:focus, + &.badge:hover, + &.badge:focus { + color: @white; + text-decoration: none; + cursor: pointer; + } +} + +// Colors +// Only give background-color difference to links (and to simplify, we don't qualifty with `a` but [href] attribute) +.label, +.badge { + // Important (red) + &-important { background-color: @errorText; } + &-important[href] { background-color: darken(@errorText, 10%); } + // Warnings (orange) + &-warning { background-color: @orange; } + &-warning[href] { background-color: darken(@orange, 10%); } + // Success (green) + &-success { background-color: @successText; } + &-success[href] { background-color: darken(@successText, 10%); } + // Info (turquoise) + &-info { background-color: @infoText; } + &-info[href] { background-color: darken(@infoText, 10%); } + // Inverse (black) + &-inverse { background-color: @grayDark; } + &-inverse[href] { background-color: darken(@grayDark, 10%); } +} + +// Quick fix for labels/badges in buttons +.btn { + .label, + .badge { + position: relative; + top: -1px; + } +} +.btn-mini { + .label, + .badge { + top: 0; + } +} diff --git a/openid-connect-server-webapp/src/main/less/layouts.less b/openid-connect-server-webapp/src/main/less/layouts.less new file mode 100644 index 0000000000..24a2062117 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/layouts.less @@ -0,0 +1,16 @@ +// +// Layouts +// -------------------------------------------------- + + +// Container (centered, fixed-width layouts) +.container { + .container-fixed(); +} + +// Fluid layouts (left aligned, with sidebar, min- & max-width content) +.container-fluid { + padding-right: @gridGutterWidth; + padding-left: @gridGutterWidth; + .clearfix(); +} \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/less/media.less b/openid-connect-server-webapp/src/main/less/media.less new file mode 100644 index 0000000000..e461e446d2 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/media.less @@ -0,0 +1,55 @@ +// Media objects +// Source: http://stubbornella.org/content/?p=497 +// -------------------------------------------------- + + +// Common styles +// ------------------------- + +// Clear the floats +.media, +.media-body { + overflow: hidden; + *overflow: visible; + zoom: 1; +} + +// Proper spacing between instances of .media +.media, +.media .media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} + +// For images and videos, set to block +.media-object { + display: block; +} + +// Reset margins on headings for tighter default spacing +.media-heading { + margin: 0 0 5px; +} + + +// Media image alignment +// ------------------------- + +.media > .pull-left { + margin-right: 10px; +} +.media > .pull-right { + margin-left: 10px; +} + + +// Media list variation +// ------------------------- + +// Undo default ul/ol styles +.media-list { + margin-left: 0; + list-style: none; +} diff --git a/openid-connect-server-webapp/src/main/less/mixins.less b/openid-connect-server-webapp/src/main/less/mixins.less new file mode 100644 index 0000000000..857561e9a0 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/mixins.less @@ -0,0 +1,702 @@ +// +// Mixins +// -------------------------------------------------- + + +// UTILITY MIXINS +// -------------------------------------------------- + +// Clearfix +// -------- +// For clearing floats like a boss h5bp.com/q +.clearfix { + *zoom: 1; + &:before, + &:after { + display: table; + content: ""; + // Fixes Opera/contenteditable bug: + // http://nicolasgallagher.com/micro-clearfix-hack/#comment-36952 + line-height: 0; + } + &:after { + clear: both; + } +} + +// Webkit-style focus +// ------------------ +.tab-focus() { + // Default + outline: thin dotted #333; + // Webkit + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +// Center-align a block level element +// ---------------------------------- +.center-block() { + display: block; + margin-left: auto; + margin-right: auto; +} + +// IE7 inline-block +// ---------------- +.ie7-inline-block() { + *display: inline; /* IE7 inline-block hack */ + *zoom: 1; +} + +// IE7 likes to collapse whitespace on either side of the inline-block elements. +// Ems because we're attempting to match the width of a space character. Left +// version is for form buttons, which typically come after other elements, and +// right version is for icons, which come before. Applying both is ok, but it will +// mean that space between those elements will be .6em (~2 space characters) in IE7, +// instead of the 1 space in other browsers. +.ie7-restore-left-whitespace() { + *margin-left: .3em; + + &:first-child { + *margin-left: 0; + } +} + +.ie7-restore-right-whitespace() { + *margin-right: .3em; +} + +// Sizing shortcuts +// ------------------------- +.size(@height, @width) { + width: @width; + height: @height; +} +.square(@size) { + .size(@size, @size); +} + +// Placeholder text +// ------------------------- +.placeholder(@color: @placeholderText) { + &:-moz-placeholder { + color: @color; + } + &:-ms-input-placeholder { + color: @color; + } + &::-webkit-input-placeholder { + color: @color; + } +} + +// Text overflow +// ------------------------- +// Requires inline-block or block for proper styling +.text-overflow() { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +// CSS image replacement +// ------------------------- +// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757 +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + + +// FONTS +// -------------------------------------------------- + +#font { + #family { + .serif() { + font-family: @serifFontFamily; + } + .sans-serif() { + font-family: @sansFontFamily; + } + .monospace() { + font-family: @monoFontFamily; + } + } + .shorthand(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { + font-size: @size; + font-weight: @weight; + line-height: @lineHeight; + } + .serif(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { + #font > #family > .serif; + #font > .shorthand(@size, @weight, @lineHeight); + } + .sans-serif(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { + #font > #family > .sans-serif; + #font > .shorthand(@size, @weight, @lineHeight); + } + .monospace(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { + #font > #family > .monospace; + #font > .shorthand(@size, @weight, @lineHeight); + } +} + + +// FORMS +// -------------------------------------------------- + +// Block level inputs +.input-block-level { + display: block; + width: 100%; + min-height: @inputHeight; // Make inputs at least the height of their button counterpart (base line-height + padding + border) + .box-sizing(border-box); // Makes inputs behave like true block-level elements +} + + + +// Mixin for form field states +.formFieldState(@textColor: #555, @borderColor: #ccc, @backgroundColor: #f5f5f5) { + // Set the text color + .control-label, + .help-block, + .help-inline { + color: @textColor; + } + // Style inputs accordingly + .checkbox, + .radio, + input, + select, + textarea { + color: @textColor; + } + input, + select, + textarea { + border-color: @borderColor; + .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work + &:focus { + border-color: darken(@borderColor, 10%); + @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@borderColor, 20%); + .box-shadow(@shadow); + } + } + // Give a small background color for input-prepend/-append + .input-prepend .add-on, + .input-append .add-on { + color: @textColor; + background-color: @backgroundColor; + border-color: @textColor; + } +} + + + +// CSS3 PROPERTIES +// -------------------------------------------------- + +// Border Radius +.border-radius(@radius) { + -webkit-border-radius: @radius; + -moz-border-radius: @radius; + border-radius: @radius; +} + +// Single Corner Border Radius +.border-top-left-radius(@radius) { + -webkit-border-top-left-radius: @radius; + -moz-border-radius-topleft: @radius; + border-top-left-radius: @radius; +} +.border-top-right-radius(@radius) { + -webkit-border-top-right-radius: @radius; + -moz-border-radius-topright: @radius; + border-top-right-radius: @radius; +} +.border-bottom-right-radius(@radius) { + -webkit-border-bottom-right-radius: @radius; + -moz-border-radius-bottomright: @radius; + border-bottom-right-radius: @radius; +} +.border-bottom-left-radius(@radius) { + -webkit-border-bottom-left-radius: @radius; + -moz-border-radius-bottomleft: @radius; + border-bottom-left-radius: @radius; +} + +// Single Side Border Radius +.border-top-radius(@radius) { + .border-top-right-radius(@radius); + .border-top-left-radius(@radius); +} +.border-right-radius(@radius) { + .border-top-right-radius(@radius); + .border-bottom-right-radius(@radius); +} +.border-bottom-radius(@radius) { + .border-bottom-right-radius(@radius); + .border-bottom-left-radius(@radius); +} +.border-left-radius(@radius) { + .border-top-left-radius(@radius); + .border-bottom-left-radius(@radius); +} + +// Drop shadows +.box-shadow(@shadow) { + -webkit-box-shadow: @shadow; + -moz-box-shadow: @shadow; + box-shadow: @shadow; +} + +// Transitions +.transition(@transition) { + -webkit-transition: @transition; + -moz-transition: @transition; + -o-transition: @transition; + transition: @transition; +} +.transition-delay(@transition-delay) { + -webkit-transition-delay: @transition-delay; + -moz-transition-delay: @transition-delay; + -o-transition-delay: @transition-delay; + transition-delay: @transition-delay; +} +.transition-duration(@transition-duration) { + -webkit-transition-duration: @transition-duration; + -moz-transition-duration: @transition-duration; + -o-transition-duration: @transition-duration; + transition-duration: @transition-duration; +} + +// Transformations +.rotate(@degrees) { + -webkit-transform: rotate(@degrees); + -moz-transform: rotate(@degrees); + -ms-transform: rotate(@degrees); + -o-transform: rotate(@degrees); + transform: rotate(@degrees); +} +.scale(@ratio) { + -webkit-transform: scale(@ratio); + -moz-transform: scale(@ratio); + -ms-transform: scale(@ratio); + -o-transform: scale(@ratio); + transform: scale(@ratio); +} +.translate(@x, @y) { + -webkit-transform: translate(@x, @y); + -moz-transform: translate(@x, @y); + -ms-transform: translate(@x, @y); + -o-transform: translate(@x, @y); + transform: translate(@x, @y); +} +.skew(@x, @y) { + -webkit-transform: skew(@x, @y); + -moz-transform: skew(@x, @y); + -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885 + -o-transform: skew(@x, @y); + transform: skew(@x, @y); + -webkit-backface-visibility: hidden; // See https://github.com/twbs/bootstrap/issues/5319 +} +.translate3d(@x, @y, @z) { + -webkit-transform: translate3d(@x, @y, @z); + -moz-transform: translate3d(@x, @y, @z); + -o-transform: translate3d(@x, @y, @z); + transform: translate3d(@x, @y, @z); +} + +// Backface visibility +// Prevent browsers from flickering when using CSS 3D transforms. +// Default value is `visible`, but can be changed to `hidden +// See git pull https://github.com/dannykeane/bootstrap.git backface-visibility for examples +.backface-visibility(@visibility){ + -webkit-backface-visibility: @visibility; + -moz-backface-visibility: @visibility; + backface-visibility: @visibility; +} + +// Background clipping +// Heads up: FF 3.6 and under need "padding" instead of "padding-box" +.background-clip(@clip) { + -webkit-background-clip: @clip; + -moz-background-clip: @clip; + background-clip: @clip; +} + +// Background sizing +.background-size(@size) { + -webkit-background-size: @size; + -moz-background-size: @size; + -o-background-size: @size; + background-size: @size; +} + + +// Box sizing +.box-sizing(@boxmodel) { + -webkit-box-sizing: @boxmodel; + -moz-box-sizing: @boxmodel; + box-sizing: @boxmodel; +} + +// User select +// For selecting text on the page +.user-select(@select) { + -webkit-user-select: @select; + -moz-user-select: @select; + -ms-user-select: @select; + -o-user-select: @select; + user-select: @select; +} + +// Resize anything +.resizable(@direction) { + resize: @direction; // Options: horizontal, vertical, both + overflow: auto; // Safari fix +} + +// CSS3 Content Columns +.content-columns(@columnCount, @columnGap: @gridGutterWidth) { + -webkit-column-count: @columnCount; + -moz-column-count: @columnCount; + column-count: @columnCount; + -webkit-column-gap: @columnGap; + -moz-column-gap: @columnGap; + column-gap: @columnGap; +} + +// Optional hyphenation +.hyphens(@mode: auto) { + word-wrap: break-word; + -webkit-hyphens: @mode; + -moz-hyphens: @mode; + -ms-hyphens: @mode; + -o-hyphens: @mode; + hyphens: @mode; +} + +// Opacity +.opacity(@opacity) { + opacity: @opacity / 100; + filter: ~"alpha(opacity=@{opacity})"; +} + + + +// BACKGROUNDS +// -------------------------------------------------- + +// Add an alphatransparency value to any background or border color (via Elyse Holladay) +#translucent { + .background(@color: @white, @alpha: 1) { + background-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha); + } + .border(@color: @white, @alpha: 1) { + border-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha); + .background-clip(padding-box); + } +} + +// Gradient Bar Colors for buttons and alerts +.gradientBar(@primaryColor, @secondaryColor, @textColor: #fff, @textShadow: 0 -1px 0 rgba(0,0,0,.25)) { + color: @textColor; + text-shadow: @textShadow; + #gradient > .vertical(@primaryColor, @secondaryColor); + border-color: @secondaryColor @secondaryColor darken(@secondaryColor, 15%); + border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) fadein(rgba(0,0,0,.1), 15%); +} + +// Gradients +#gradient { + .horizontal(@startColor: #555, @endColor: #333) { + background-color: @endColor; + background-image: -moz-linear-gradient(left, @startColor, @endColor); // FF 3.6+ + background-image: -webkit-gradient(linear, 0 0, 100% 0, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+ + background-image: -webkit-linear-gradient(left, @startColor, @endColor); // Safari 5.1+, Chrome 10+ + background-image: -o-linear-gradient(left, @startColor, @endColor); // Opera 11.10 + background-image: linear-gradient(to right, @startColor, @endColor); // Standard, IE10 + background-repeat: repeat-x; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",argb(@startColor),argb(@endColor))); // IE9 and down + } + .vertical(@startColor: #555, @endColor: #333) { + background-color: mix(@startColor, @endColor, 60%); + background-image: -moz-linear-gradient(top, @startColor, @endColor); // FF 3.6+ + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+ + background-image: -webkit-linear-gradient(top, @startColor, @endColor); // Safari 5.1+, Chrome 10+ + background-image: -o-linear-gradient(top, @startColor, @endColor); // Opera 11.10 + background-image: linear-gradient(to bottom, @startColor, @endColor); // Standard, IE10 + background-repeat: repeat-x; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down + } + .directional(@startColor: #555, @endColor: #333, @deg: 45deg) { + background-color: @endColor; + background-repeat: repeat-x; + background-image: -moz-linear-gradient(@deg, @startColor, @endColor); // FF 3.6+ + background-image: -webkit-linear-gradient(@deg, @startColor, @endColor); // Safari 5.1+, Chrome 10+ + background-image: -o-linear-gradient(@deg, @startColor, @endColor); // Opera 11.10 + background-image: linear-gradient(@deg, @startColor, @endColor); // Standard, IE10 + } + .horizontal-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) { + background-color: mix(@midColor, @endColor, 80%); + background-image: -webkit-gradient(left, linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor)); + background-image: -webkit-linear-gradient(left, @startColor, @midColor @colorStop, @endColor); + background-image: -moz-linear-gradient(left, @startColor, @midColor @colorStop, @endColor); + background-image: -o-linear-gradient(left, @startColor, @midColor @colorStop, @endColor); + background-image: linear-gradient(to right, @startColor, @midColor @colorStop, @endColor); + background-repeat: no-repeat; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down, gets no color-stop at all for proper fallback + } + + .vertical-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) { + background-color: mix(@midColor, @endColor, 80%); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor)); + background-image: -webkit-linear-gradient(@startColor, @midColor @colorStop, @endColor); + background-image: -moz-linear-gradient(top, @startColor, @midColor @colorStop, @endColor); + background-image: -o-linear-gradient(@startColor, @midColor @colorStop, @endColor); + background-image: linear-gradient(@startColor, @midColor @colorStop, @endColor); + background-repeat: no-repeat; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor),argb(@endColor))); // IE9 and down, gets no color-stop at all for proper fallback + } + .radial(@innerColor: #555, @outerColor: #333) { + background-color: @outerColor; + background-image: -webkit-gradient(radial, center center, 0, center center, 460, from(@innerColor), to(@outerColor)); + background-image: -webkit-radial-gradient(circle, @innerColor, @outerColor); + background-image: -moz-radial-gradient(circle, @innerColor, @outerColor); + background-image: -o-radial-gradient(circle, @innerColor, @outerColor); + background-repeat: no-repeat; + } + .striped(@color: #555, @angle: 45deg) { + background-color: @color; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, rgba(255,255,255,.15)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255,255,255,.15)), color-stop(.75, rgba(255,255,255,.15)), color-stop(.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); + } +} +// Reset filters for IE +.reset-filter() { + filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)")); +} + + + +// COMPONENT MIXINS +// -------------------------------------------------- + +// Horizontal dividers +// ------------------------- +// Dividers (basically an hr) within dropdowns and nav lists +.nav-divider(@top: #e5e5e5, @bottom: @white) { + // IE7 needs a set width since we gave a height. Restricting just + // to IE7 to keep the 1px left/right space in other browsers. + // It is unclear where IE is getting the extra space that we need + // to negative-margin away, but so it goes. + *width: 100%; + height: 1px; + margin: ((@baseLineHeight / 2) - 1) 1px; // 8px 1px + *margin: -5px 0 5px; + overflow: hidden; + background-color: @top; + border-bottom: 1px solid @bottom; +} + +// Button backgrounds +// ------------------ +.buttonBackground(@startColor, @endColor, @textColor: #fff, @textShadow: 0 -1px 0 rgba(0,0,0,.25)) { + // gradientBar will set the background to a pleasing blend of these, to support IE<=9 + .gradientBar(@startColor, @endColor, @textColor, @textShadow); + *background-color: @endColor; /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + .reset-filter(); + + // in these cases the gradient won't cover the background, so we override + &:hover, &:focus, &:active, &.active, &.disabled, &[disabled] { + color: @textColor; + background-color: @endColor; + *background-color: darken(@endColor, 5%); + } + + // IE 7 + 8 can't handle box-shadow to show active, so we darken a bit ourselves + &:active, + &.active { + background-color: darken(@endColor, 10%) e("\9"); + } +} + +// Navbar vertical align +// ------------------------- +// Vertically center elements in the navbar. +// Example: an element has a height of 30px, so write out `.navbarVerticalAlign(30px);` to calculate the appropriate top margin. +.navbarVerticalAlign(@elementHeight) { + margin-top: (@navbarHeight - @elementHeight) / 2; +} + + + +// Grid System +// ----------- + +// Centered container element +.container-fixed() { + margin-right: auto; + margin-left: auto; + .clearfix(); +} + +// Table columns +.tableColumns(@columnSpan: 1) { + float: none; // undo default grid column styles + width: ((@gridColumnWidth) * @columnSpan) + (@gridGutterWidth * (@columnSpan - 1)) - 16; // 16 is total padding on left and right of table cells + margin-left: 0; // undo default grid column styles +} + +// Make a Grid +// Use .makeRow and .makeColumn to assign semantic layouts grid system behavior +.makeRow() { + margin-left: @gridGutterWidth * -1; + .clearfix(); +} +.makeColumn(@columns: 1, @offset: 0) { + float: left; + margin-left: (@gridColumnWidth * @offset) + (@gridGutterWidth * (@offset - 1)) + (@gridGutterWidth * 2); + width: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1)); +} + +// The Grid +#grid { + + .core (@gridColumnWidth, @gridGutterWidth) { + + .spanX (@index) when (@index > 0) { + .span@{index} { .span(@index); } + .spanX(@index - 1); + } + .spanX (0) {} + + .offsetX (@index) when (@index > 0) { + .offset@{index} { .offset(@index); } + .offsetX(@index - 1); + } + .offsetX (0) {} + + .offset (@columns) { + margin-left: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns + 1)); + } + + .span (@columns) { + width: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1)); + } + + .row { + margin-left: @gridGutterWidth * -1; + .clearfix(); + } + + [class*="span"] { + float: left; + min-height: 1px; // prevent collapsing columns + margin-left: @gridGutterWidth; + } + + // Set the container width, and override it for fixed navbars in media queries + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { .span(@gridColumns); } + + // generate .spanX and .offsetX + .spanX (@gridColumns); + .offsetX (@gridColumns); + + } + + .fluid (@fluidGridColumnWidth, @fluidGridGutterWidth) { + + .spanX (@index) when (@index > 0) { + .span@{index} { .span(@index); } + .spanX(@index - 1); + } + .spanX (0) {} + + .offsetX (@index) when (@index > 0) { + .offset@{index} { .offset(@index); } + .offset@{index}:first-child { .offsetFirstChild(@index); } + .offsetX(@index - 1); + } + .offsetX (0) {} + + .offset (@columns) { + margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) + (@fluidGridGutterWidth*2); + *margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%) + (@fluidGridGutterWidth*2) - (.5 / @gridRowWidth * 100 * 1%); + } + + .offsetFirstChild (@columns) { + margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) + (@fluidGridGutterWidth); + *margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%) + @fluidGridGutterWidth - (.5 / @gridRowWidth * 100 * 1%); + } + + .span (@columns) { + width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)); + *width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%); + } + + .row-fluid { + width: 100%; + .clearfix(); + [class*="span"] { + .input-block-level(); + float: left; + margin-left: @fluidGridGutterWidth; + *margin-left: @fluidGridGutterWidth - (.5 / @gridRowWidth * 100 * 1%); + } + [class*="span"]:first-child { + margin-left: 0; + } + + // Space grid-sized controls properly if multiple per line + .controls-row [class*="span"] + [class*="span"] { + margin-left: @fluidGridGutterWidth; + } + + // generate .spanX and .offsetX + .spanX (@gridColumns); + .offsetX (@gridColumns); + } + + } + + .input(@gridColumnWidth, @gridGutterWidth) { + + .spanX (@index) when (@index > 0) { + input.span@{index}, textarea.span@{index}, .uneditable-input.span@{index} { .span(@index); } + .spanX(@index - 1); + } + .spanX (0) {} + + .span(@columns) { + width: ((@gridColumnWidth) * @columns) + (@gridGutterWidth * (@columns - 1)) - 14; + } + + input, + textarea, + .uneditable-input { + margin-left: 0; // override margin-left from core grid system + } + + // Space grid-sized controls properly if multiple per line + .controls-row [class*="span"] + [class*="span"] { + margin-left: @gridGutterWidth; + } + + // generate .spanX + .spanX (@gridColumns); + + } +} diff --git a/openid-connect-server-webapp/src/main/less/modals.less b/openid-connect-server-webapp/src/main/less/modals.less new file mode 100644 index 0000000000..8e272d409f --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/modals.less @@ -0,0 +1,95 @@ +// +// Modals +// -------------------------------------------------- + +// Background +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: @zindexModalBackdrop; + background-color: @black; + // Fade for backdrop + &.fade { opacity: 0; } +} + +.modal-backdrop, +.modal-backdrop.fade.in { + .opacity(80); +} + +// Base modal +.modal { + position: fixed; + top: 10%; + left: 50%; + z-index: @zindexModal; + width: 560px; + margin-left: -280px; + background-color: @white; + border: 1px solid #999; + border: 1px solid rgba(0,0,0,.3); + *border: 1px solid #999; /* IE6-7 */ + .border-radius(6px); + .box-shadow(0 3px 7px rgba(0,0,0,0.3)); + .background-clip(padding-box); + // Remove focus outline from opened modal + outline: none; + + &.fade { + .transition(e('opacity .3s linear, top .3s ease-out')); + top: -25%; + } + &.fade.in { top: 10%; } +} +.modal-header { + padding: 9px 15px; + border-bottom: 1px solid #eee; + // Close icon + .close { margin-top: 2px; } + // Heading + h3 { + margin: 0; + line-height: 30px; + } +} + +// Body (where all modal content resides) +.modal-body { + position: relative; + overflow-y: auto; + max-height: 400px; + padding: 15px; +} +// Remove bottom margin if need be +.modal-form { + margin-bottom: 0; +} + +// Footer (for actions) +.modal-footer { + padding: 14px 15px 15px; + margin-bottom: 0; + text-align: right; // right align buttons + background-color: #f5f5f5; + border-top: 1px solid #ddd; + .border-radius(0 0 6px 6px); + .box-shadow(inset 0 1px 0 @white); + .clearfix(); // clear it in case folks use .pull-* classes on buttons + + // Properly space out buttons + .btn + .btn { + margin-left: 5px; + margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs + } + // but override that for button groups + .btn-group .btn + .btn { + margin-left: -1px; + } + // and override it for block buttons as well + .btn-block + .btn-block { + margin-left: 0; + } +} diff --git a/openid-connect-server-webapp/src/main/less/navbar.less b/openid-connect-server-webapp/src/main/less/navbar.less new file mode 100644 index 0000000000..93d09bcad0 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/navbar.less @@ -0,0 +1,497 @@ +// +// Navbars (Redux) +// -------------------------------------------------- + + +// COMMON STYLES +// ------------- + +// Base class and wrapper +.navbar { + overflow: visible; + margin-bottom: @baseLineHeight; + + // Fix for IE7's bad z-indexing so dropdowns don't appear below content that follows the navbar + *position: relative; + *z-index: 2; +} + +// Inner for background effects +// Gradient is applied to its own element because overflow visible is not honored by IE when filter is present +.navbar-inner { + min-height: @navbarHeight; + padding-left: 20px; + padding-right: 20px; + #gradient > .vertical(@navbarBackgroundHighlight, @navbarBackground); + border: 1px solid @navbarBorder; + .border-radius(@baseBorderRadius); + .box-shadow(0 1px 4px rgba(0,0,0,.065)); + + // Prevent floats from breaking the navbar + .clearfix(); +} + +// Set width to auto for default container +// We then reset it for fixed navbars in the #gridSystem mixin +.navbar .container { + width: auto; +} + +// Override the default collapsed state +.nav-collapse.collapse { + height: auto; + overflow: visible; +} + + +// Brand: website or project name +// ------------------------- +.navbar .brand { + float: left; + display: block; + // Vertically center the text given @navbarHeight + padding: ((@navbarHeight - @baseLineHeight) / 2) 20px ((@navbarHeight - @baseLineHeight) / 2); + margin-left: -20px; // negative indent to left-align the text down the page + font-size: 20px; + font-weight: 200; + color: @navbarBrandColor; + text-shadow: 0 1px 0 @navbarBackgroundHighlight; + &:hover, + &:focus { + text-decoration: none; + } +} + +// Plain text in topbar +// ------------------------- +.navbar-text { + margin-bottom: 0; + line-height: @navbarHeight; + color: @navbarText; +} + +// Janky solution for now to account for links outside the .nav +// ------------------------- +.navbar-link { + color: @navbarLinkColor; + &:hover, + &:focus { + color: @navbarLinkColorHover; + } +} + +// Dividers in navbar +// ------------------------- +.navbar .divider-vertical { + height: @navbarHeight; + margin: 0 9px; + border-left: 1px solid @navbarBackground; + border-right: 1px solid @navbarBackgroundHighlight; +} + +// Buttons in navbar +// ------------------------- +.navbar .btn, +.navbar .btn-group { + .navbarVerticalAlign(30px); // Vertically center in navbar +} +.navbar .btn-group .btn, +.navbar .input-prepend .btn, +.navbar .input-append .btn, +.navbar .input-prepend .btn-group, +.navbar .input-append .btn-group { + margin-top: 0; // then undo the margin here so we don't accidentally double it +} + +// Navbar forms +// ------------------------- +.navbar-form { + margin-bottom: 0; // remove default bottom margin + .clearfix(); + input, + select, + .radio, + .checkbox { + .navbarVerticalAlign(30px); // Vertically center in navbar + } + input, + select, + .btn { + display: inline-block; + margin-bottom: 0; + } + input[type="image"], + input[type="checkbox"], + input[type="radio"] { + margin-top: 3px; + } + .input-append, + .input-prepend { + margin-top: 5px; + white-space: nowrap; // preven two items from separating within a .navbar-form that has .pull-left + input { + margin-top: 0; // remove the margin on top since it's on the parent + } + } +} + +// Navbar search +// ------------------------- +.navbar-search { + position: relative; + float: left; + .navbarVerticalAlign(30px); // Vertically center in navbar + margin-bottom: 0; + .search-query { + margin-bottom: 0; + padding: 4px 14px; + #font > .sans-serif(13px, normal, 1); + .border-radius(15px); // redeclare because of specificity of the type attribute + } +} + + + +// Static navbar +// ------------------------- + +.navbar-static-top { + position: static; + margin-bottom: 0; // remove 18px margin for default navbar + .navbar-inner { + .border-radius(0); + } +} + + + +// Fixed navbar +// ------------------------- + +// Shared (top/bottom) styles +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: @zindexFixedNavbar; + margin-bottom: 0; // remove 18px margin for default navbar +} +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + border-width: 0 0 1px; +} +.navbar-fixed-bottom .navbar-inner { + border-width: 1px 0 0; +} +.navbar-fixed-top .navbar-inner, +.navbar-fixed-bottom .navbar-inner { + padding-left: 0; + padding-right: 0; + .border-radius(0); +} + +// Reset container width +// Required here as we reset the width earlier on and the grid mixins don't override early enough +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + #grid > .core > .span(@gridColumns); +} + +// Fixed to top +.navbar-fixed-top { + top: 0; +} +.navbar-fixed-top, +.navbar-static-top { + .navbar-inner { + .box-shadow(~"0 1px 10px rgba(0,0,0,.1)"); + } +} + +// Fixed to bottom +.navbar-fixed-bottom { + bottom: 0; + .navbar-inner { + .box-shadow(~"0 -1px 10px rgba(0,0,0,.1)"); + } +} + + + +// NAVIGATION +// ---------- + +.navbar .nav { + position: relative; + left: 0; + display: block; + float: left; + margin: 0 10px 0 0; +} +.navbar .nav.pull-right { + float: right; // redeclare due to specificity + margin-right: 0; // remove margin on float right nav +} +.navbar .nav > li { + float: left; +} + +// Links +.navbar .nav > li > a { + float: none; + // Vertically center the text given @navbarHeight + padding: ((@navbarHeight - @baseLineHeight) / 2) 15px ((@navbarHeight - @baseLineHeight) / 2); + color: @navbarLinkColor; + text-decoration: none; + text-shadow: 0 1px 0 @navbarBackgroundHighlight; +} +.navbar .nav .dropdown-toggle .caret { + margin-top: 8px; +} + +// Hover/focus +.navbar .nav > li > a:focus, +.navbar .nav > li > a:hover { + background-color: @navbarLinkBackgroundHover; // "transparent" is default to differentiate :hover/:focus from .active + color: @navbarLinkColorHover; + text-decoration: none; +} + +// Active nav items +.navbar .nav > .active > a, +.navbar .nav > .active > a:hover, +.navbar .nav > .active > a:focus { + color: @navbarLinkColorActive; + text-decoration: none; + background-color: @navbarLinkBackgroundActive; + .box-shadow(inset 0 3px 8px rgba(0,0,0,.125)); +} + +// Navbar button for toggling navbar items in responsive layouts +// These definitions need to come after '.navbar .btn' +.navbar .btn-navbar { + display: none; + float: right; + padding: 7px 10px; + margin-left: 5px; + margin-right: 5px; + .buttonBackground(darken(@navbarBackgroundHighlight, 5%), darken(@navbarBackground, 5%)); + .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075)"); +} +.navbar .btn-navbar .icon-bar { + display: block; + width: 18px; + height: 2px; + background-color: #f5f5f5; + .border-radius(1px); + .box-shadow(0 1px 0 rgba(0,0,0,.25)); +} +.btn-navbar .icon-bar + .icon-bar { + margin-top: 3px; +} + + + +// Dropdown menus +// -------------- + +// Menu position and menu carets +.navbar .nav > li > .dropdown-menu { + &:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: @dropdownBorder; + position: absolute; + top: -7px; + left: 9px; + } + &:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid @dropdownBackground; + position: absolute; + top: -6px; + left: 10px; + } +} +// Menu position and menu caret support for dropups via extra dropup class +.navbar-fixed-bottom .nav > li > .dropdown-menu { + &:before { + border-top: 7px solid #ccc; + border-top-color: @dropdownBorder; + border-bottom: 0; + bottom: -7px; + top: auto; + } + &:after { + border-top: 6px solid @dropdownBackground; + border-bottom: 0; + bottom: -6px; + top: auto; + } +} + +// Caret should match text color on hover/focus +.navbar .nav li.dropdown > a:hover .caret, +.navbar .nav li.dropdown > a:focus .caret { + border-top-color: @navbarLinkColorHover; + border-bottom-color: @navbarLinkColorHover; +} + +// Remove background color from open dropdown +.navbar .nav li.dropdown.open > .dropdown-toggle, +.navbar .nav li.dropdown.active > .dropdown-toggle, +.navbar .nav li.dropdown.open.active > .dropdown-toggle { + background-color: @navbarLinkBackgroundActive; + color: @navbarLinkColorActive; +} +.navbar .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: @navbarLinkColor; + border-bottom-color: @navbarLinkColor; +} +.navbar .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: @navbarLinkColorActive; + border-bottom-color: @navbarLinkColorActive; +} + +// Right aligned menus need alt position +.navbar .pull-right > li > .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right { + left: auto; + right: 0; + &:before { + left: auto; + right: 12px; + } + &:after { + left: auto; + right: 13px; + } + .dropdown-menu { + left: auto; + right: 100%; + margin-left: 0; + margin-right: -1px; + .border-radius(6px 0 6px 6px); + } +} + + +// Inverted navbar +// ------------------------- + +.navbar-inverse { + + .navbar-inner { + #gradient > .vertical(@navbarInverseBackgroundHighlight, @navbarInverseBackground); + border-color: @navbarInverseBorder; + } + + .brand, + .nav > li > a { + color: @navbarInverseLinkColor; + text-shadow: 0 -1px 0 rgba(0,0,0,.25); + &:hover, + &:focus { + color: @navbarInverseLinkColorHover; + } + } + + .brand { + color: @navbarInverseBrandColor; + } + + .navbar-text { + color: @navbarInverseText; + } + + .nav > li > a:focus, + .nav > li > a:hover { + background-color: @navbarInverseLinkBackgroundHover; + color: @navbarInverseLinkColorHover; + } + + .nav .active > a, + .nav .active > a:hover, + .nav .active > a:focus { + color: @navbarInverseLinkColorActive; + background-color: @navbarInverseLinkBackgroundActive; + } + + // Inline text links + .navbar-link { + color: @navbarInverseLinkColor; + &:hover, + &:focus { + color: @navbarInverseLinkColorHover; + } + } + + // Dividers in navbar + .divider-vertical { + border-left-color: @navbarInverseBackground; + border-right-color: @navbarInverseBackgroundHighlight; + } + + // Dropdowns + .nav li.dropdown.open > .dropdown-toggle, + .nav li.dropdown.active > .dropdown-toggle, + .nav li.dropdown.open.active > .dropdown-toggle { + background-color: @navbarInverseLinkBackgroundActive; + color: @navbarInverseLinkColorActive; + } + .nav li.dropdown > a:hover .caret, + .nav li.dropdown > a:focus .caret { + border-top-color: @navbarInverseLinkColorActive; + border-bottom-color: @navbarInverseLinkColorActive; + } + .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: @navbarInverseLinkColor; + border-bottom-color: @navbarInverseLinkColor; + } + .nav li.dropdown.open > .dropdown-toggle .caret, + .nav li.dropdown.active > .dropdown-toggle .caret, + .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: @navbarInverseLinkColorActive; + border-bottom-color: @navbarInverseLinkColorActive; + } + + // Navbar search + .navbar-search { + .search-query { + color: @white; + background-color: @navbarInverseSearchBackground; + border-color: @navbarInverseSearchBorder; + .box-shadow(~"inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15)"); + .transition(none); + .placeholder(@navbarInverseSearchPlaceholderColor); + + // Focus states (we use .focused since IE7-8 and down doesn't support :focus) + &:focus, + &.focused { + padding: 5px 15px; + color: @grayDark; + text-shadow: 0 1px 0 @white; + background-color: @navbarInverseSearchBackgroundFocus; + border: 0; + .box-shadow(0 0 3px rgba(0,0,0,.15)); + outline: 0; + } + } + } + + // Navbar collapse button + .btn-navbar { + .buttonBackground(darken(@navbarInverseBackgroundHighlight, 5%), darken(@navbarInverseBackground, 5%)); + } + +} diff --git a/openid-connect-server-webapp/src/main/less/navs.less b/openid-connect-server-webapp/src/main/less/navs.less new file mode 100644 index 0000000000..01cd805bde --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/navs.less @@ -0,0 +1,409 @@ +// +// Navs +// -------------------------------------------------- + + +// BASE CLASS +// ---------- + +.nav { + margin-left: 0; + margin-bottom: @baseLineHeight; + list-style: none; +} + +// Make links block level +.nav > li > a { + display: block; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: @grayLighter; +} + +// Prevent IE8 from misplacing imgs +// See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989 +.nav > li > a > img { + max-width: none; +} + +// Redeclare pull classes because of specifity +.nav > .pull-right { + float: right; +} + +// Nav headers (for dropdowns and lists) +.nav-header { + display: block; + padding: 3px 15px; + font-size: 11px; + font-weight: bold; + line-height: @baseLineHeight; + color: @grayLight; + text-shadow: 0 1px 0 rgba(255,255,255,.5); + text-transform: uppercase; +} +// Space them out when they follow another list item (link) +.nav li + .nav-header { + margin-top: 9px; +} + + + +// NAV LIST +// -------- + +.nav-list { + padding-left: 15px; + padding-right: 15px; + margin-bottom: 0; +} +.nav-list > li > a, +.nav-list .nav-header { + margin-left: -15px; + margin-right: -15px; + text-shadow: 0 1px 0 rgba(255,255,255,.5); +} +.nav-list > li > a { + padding: 3px 15px; +} +.nav-list > .active > a, +.nav-list > .active > a:hover, +.nav-list > .active > a:focus { + color: @white; + text-shadow: 0 -1px 0 rgba(0,0,0,.2); + background-color: @linkColor; +} +.nav-list [class^="icon-"], +.nav-list [class*=" icon-"] { + margin-right: 2px; +} +// Dividers (basically an hr) within the dropdown +.nav-list .divider { + .nav-divider(); +} + + + +// TABS AND PILLS +// ------------- + +// Common styles +.nav-tabs, +.nav-pills { + .clearfix(); +} +.nav-tabs > li, +.nav-pills > li { + float: left; +} +.nav-tabs > li > a, +.nav-pills > li > a { + padding-right: 12px; + padding-left: 12px; + margin-right: 2px; + line-height: 14px; // keeps the overall height an even number +} + +// TABS +// ---- + +// Give the tabs something to sit on +.nav-tabs { + border-bottom: 1px solid #ddd; +} +// Make the list-items overlay the bottom border +.nav-tabs > li { + margin-bottom: -1px; +} +// Actual tabs (as links) +.nav-tabs > li > a { + padding-top: 8px; + padding-bottom: 8px; + line-height: @baseLineHeight; + border: 1px solid transparent; + .border-radius(4px 4px 0 0); + &:hover, + &:focus { + border-color: @grayLighter @grayLighter #ddd; + } +} +// Active state, and it's :hover/:focus to override normal :hover/:focus +.nav-tabs > .active > a, +.nav-tabs > .active > a:hover, +.nav-tabs > .active > a:focus { + color: @gray; + background-color: @bodyBackground; + border: 1px solid #ddd; + border-bottom-color: transparent; + cursor: default; +} + + +// PILLS +// ----- + +// Links rendered as pills +.nav-pills > li > a { + padding-top: 8px; + padding-bottom: 8px; + margin-top: 2px; + margin-bottom: 2px; + .border-radius(5px); +} + +// Active state +.nav-pills > .active > a, +.nav-pills > .active > a:hover, +.nav-pills > .active > a:focus { + color: @white; + background-color: @linkColor; +} + + + +// STACKED NAV +// ----------- + +// Stacked tabs and pills +.nav-stacked > li { + float: none; +} +.nav-stacked > li > a { + margin-right: 0; // no need for the gap between nav items +} + +// Tabs +.nav-tabs.nav-stacked { + border-bottom: 0; +} +.nav-tabs.nav-stacked > li > a { + border: 1px solid #ddd; + .border-radius(0); +} +.nav-tabs.nav-stacked > li:first-child > a { + .border-top-radius(4px); +} +.nav-tabs.nav-stacked > li:last-child > a { + .border-bottom-radius(4px); +} +.nav-tabs.nav-stacked > li > a:hover, +.nav-tabs.nav-stacked > li > a:focus { + border-color: #ddd; + z-index: 2; +} + +// Pills +.nav-pills.nav-stacked > li > a { + margin-bottom: 3px; +} +.nav-pills.nav-stacked > li:last-child > a { + margin-bottom: 1px; // decrease margin to match sizing of stacked tabs +} + + + +// DROPDOWNS +// --------- + +.nav-tabs .dropdown-menu { + .border-radius(0 0 6px 6px); // remove the top rounded corners here since there is a hard edge above the menu +} +.nav-pills .dropdown-menu { + .border-radius(6px); // make rounded corners match the pills +} + +// Default dropdown links +// ------------------------- +// Make carets use linkColor to start +.nav .dropdown-toggle .caret { + border-top-color: @linkColor; + border-bottom-color: @linkColor; + margin-top: 6px; +} +.nav .dropdown-toggle:hover .caret, +.nav .dropdown-toggle:focus .caret { + border-top-color: @linkColorHover; + border-bottom-color: @linkColorHover; +} +/* move down carets for tabs */ +.nav-tabs .dropdown-toggle .caret { + margin-top: 8px; +} + +// Active dropdown links +// ------------------------- +.nav .active .dropdown-toggle .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} +.nav-tabs .active .dropdown-toggle .caret { + border-top-color: @gray; + border-bottom-color: @gray; +} + +// Active:hover/:focus dropdown links +// ------------------------- +.nav > .dropdown.active > a:hover, +.nav > .dropdown.active > a:focus { + cursor: pointer; +} + +// Open dropdowns +// ------------------------- +.nav-tabs .open .dropdown-toggle, +.nav-pills .open .dropdown-toggle, +.nav > li.dropdown.open.active > a:hover, +.nav > li.dropdown.open.active > a:focus { + color: @white; + background-color: @grayLight; + border-color: @grayLight; +} +.nav li.dropdown.open .caret, +.nav li.dropdown.open.active .caret, +.nav li.dropdown.open a:hover .caret, +.nav li.dropdown.open a:focus .caret { + border-top-color: @white; + border-bottom-color: @white; + .opacity(100); +} + +// Dropdowns in stacked tabs +.tabs-stacked .open > a:hover, +.tabs-stacked .open > a:focus { + border-color: @grayLight; +} + + + +// TABBABLE +// -------- + + +// COMMON STYLES +// ------------- + +// Clear any floats +.tabbable { + .clearfix(); +} +.tab-content { + overflow: auto; // prevent content from running below tabs +} + +// Remove border on bottom, left, right +.tabs-below > .nav-tabs, +.tabs-right > .nav-tabs, +.tabs-left > .nav-tabs { + border-bottom: 0; +} + +// Show/hide tabbable areas +.tab-content > .tab-pane, +.pill-content > .pill-pane { + display: none; +} +.tab-content > .active, +.pill-content > .active { + display: block; +} + + +// BOTTOM +// ------ + +.tabs-below > .nav-tabs { + border-top: 1px solid #ddd; +} +.tabs-below > .nav-tabs > li { + margin-top: -1px; + margin-bottom: 0; +} +.tabs-below > .nav-tabs > li > a { + .border-radius(0 0 4px 4px); + &:hover, + &:focus { + border-bottom-color: transparent; + border-top-color: #ddd; + } +} +.tabs-below > .nav-tabs > .active > a, +.tabs-below > .nav-tabs > .active > a:hover, +.tabs-below > .nav-tabs > .active > a:focus { + border-color: transparent #ddd #ddd #ddd; +} + +// LEFT & RIGHT +// ------------ + +// Common styles +.tabs-left > .nav-tabs > li, +.tabs-right > .nav-tabs > li { + float: none; +} +.tabs-left > .nav-tabs > li > a, +.tabs-right > .nav-tabs > li > a { + min-width: 74px; + margin-right: 0; + margin-bottom: 3px; +} + +// Tabs on the left +.tabs-left > .nav-tabs { + float: left; + margin-right: 19px; + border-right: 1px solid #ddd; +} +.tabs-left > .nav-tabs > li > a { + margin-right: -1px; + .border-radius(4px 0 0 4px); +} +.tabs-left > .nav-tabs > li > a:hover, +.tabs-left > .nav-tabs > li > a:focus { + border-color: @grayLighter #ddd @grayLighter @grayLighter; +} +.tabs-left > .nav-tabs .active > a, +.tabs-left > .nav-tabs .active > a:hover, +.tabs-left > .nav-tabs .active > a:focus { + border-color: #ddd transparent #ddd #ddd; + *border-right-color: @white; +} + +// Tabs on the right +.tabs-right > .nav-tabs { + float: right; + margin-left: 19px; + border-left: 1px solid #ddd; +} +.tabs-right > .nav-tabs > li > a { + margin-left: -1px; + .border-radius(0 4px 4px 0); +} +.tabs-right > .nav-tabs > li > a:hover, +.tabs-right > .nav-tabs > li > a:focus { + border-color: @grayLighter @grayLighter @grayLighter #ddd; +} +.tabs-right > .nav-tabs .active > a, +.tabs-right > .nav-tabs .active > a:hover, +.tabs-right > .nav-tabs .active > a:focus { + border-color: #ddd #ddd #ddd transparent; + *border-left-color: @white; +} + + + +// DISABLED STATES +// --------------- + +// Gray out text +.nav > .disabled > a { + color: @grayLight; +} +// Nuke hover/focus effects +.nav > .disabled > a:hover, +.nav > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + cursor: default; +} diff --git a/openid-connect-server-webapp/src/main/less/pager.less b/openid-connect-server-webapp/src/main/less/pager.less new file mode 100644 index 0000000000..1476188297 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/pager.less @@ -0,0 +1,43 @@ +// +// Pager pagination +// -------------------------------------------------- + + +.pager { + margin: @baseLineHeight 0; + list-style: none; + text-align: center; + .clearfix(); +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + .border-radius(15px); +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #f5f5f5; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: @grayLight; + background-color: #fff; + cursor: default; +} \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/less/pagination.less b/openid-connect-server-webapp/src/main/less/pagination.less new file mode 100644 index 0000000000..a789db2d28 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/pagination.less @@ -0,0 +1,123 @@ +// +// Pagination (multiple pages) +// -------------------------------------------------- + +// Space out pagination from surrounding content +.pagination { + margin: @baseLineHeight 0; +} + +.pagination ul { + // Allow for text-based alignment + display: inline-block; + .ie7-inline-block(); + // Reset default ul styles + margin-left: 0; + margin-bottom: 0; + // Visuals + .border-radius(@baseBorderRadius); + .box-shadow(0 1px 2px rgba(0,0,0,.05)); +} +.pagination ul > li { + display: inline; // Remove list-style and block-level defaults +} +.pagination ul > li > a, +.pagination ul > li > span { + float: left; // Collapse white-space + padding: 4px 12px; + line-height: @baseLineHeight; + text-decoration: none; + background-color: @paginationBackground; + border: 1px solid @paginationBorder; + border-left-width: 0; +} +.pagination ul > li > a:hover, +.pagination ul > li > a:focus, +.pagination ul > .active > a, +.pagination ul > .active > span { + background-color: @paginationActiveBackground; +} +.pagination ul > .active > a, +.pagination ul > .active > span { + color: @grayLight; + cursor: default; +} +.pagination ul > .disabled > span, +.pagination ul > .disabled > a, +.pagination ul > .disabled > a:hover, +.pagination ul > .disabled > a:focus { + color: @grayLight; + background-color: transparent; + cursor: default; +} +.pagination ul > li:first-child > a, +.pagination ul > li:first-child > span { + border-left-width: 1px; + .border-left-radius(@baseBorderRadius); +} +.pagination ul > li:last-child > a, +.pagination ul > li:last-child > span { + .border-right-radius(@baseBorderRadius); +} + + +// Alignment +// -------------------------------------------------- + +.pagination-centered { + text-align: center; +} +.pagination-right { + text-align: right; +} + + +// Sizing +// -------------------------------------------------- + +// Large +.pagination-large { + ul > li > a, + ul > li > span { + padding: @paddingLarge; + font-size: @fontSizeLarge; + } + ul > li:first-child > a, + ul > li:first-child > span { + .border-left-radius(@borderRadiusLarge); + } + ul > li:last-child > a, + ul > li:last-child > span { + .border-right-radius(@borderRadiusLarge); + } +} + +// Small and mini +.pagination-mini, +.pagination-small { + ul > li:first-child > a, + ul > li:first-child > span { + .border-left-radius(@borderRadiusSmall); + } + ul > li:last-child > a, + ul > li:last-child > span { + .border-right-radius(@borderRadiusSmall); + } +} + +// Small +.pagination-small { + ul > li > a, + ul > li > span { + padding: @paddingSmall; + font-size: @fontSizeSmall; + } +} +// Mini +.pagination-mini { + ul > li > a, + ul > li > span { + padding: @paddingMini; + font-size: @fontSizeMini; + } +} diff --git a/openid-connect-server-webapp/src/main/less/popovers.less b/openid-connect-server-webapp/src/main/less/popovers.less new file mode 100644 index 0000000000..aae35c8cd5 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/popovers.less @@ -0,0 +1,133 @@ +// +// Popovers +// -------------------------------------------------- + + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: @zindexPopover; + display: none; + max-width: 276px; + padding: 1px; + text-align: left; // Reset given new insertion method + background-color: @popoverBackground; + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0,0,0,.2); + .border-radius(6px); + .box-shadow(0 5px 10px rgba(0,0,0,.2)); + + // Overrides for proper insertion + white-space: normal; + + // Offset the popover to account for the popover arrow + &.top { margin-top: -10px; } + &.right { margin-left: 10px; } + &.bottom { margin-top: 10px; } + &.left { margin-left: -10px; } +} + +.popover-title { + margin: 0; // reset heading margin + padding: 8px 14px; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: @popoverTitleBackground; + border-bottom: 1px solid darken(@popoverTitleBackground, 5%); + .border-radius(5px 5px 0 0); + + &:empty { + display: none; + } +} + +.popover-content { + padding: 9px 14px; +} + +// Arrows +// +// .arrow is outer, .arrow:after is inner + +.popover .arrow, +.popover .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover .arrow { + border-width: @popoverArrowOuterWidth; +} +.popover .arrow:after { + border-width: @popoverArrowWidth; + content: ""; +} + +.popover { + &.top .arrow { + left: 50%; + margin-left: -@popoverArrowOuterWidth; + border-bottom-width: 0; + border-top-color: #999; // IE8 fallback + border-top-color: @popoverArrowOuterColor; + bottom: -@popoverArrowOuterWidth; + &:after { + bottom: 1px; + margin-left: -@popoverArrowWidth; + border-bottom-width: 0; + border-top-color: @popoverArrowColor; + } + } + &.right .arrow { + top: 50%; + left: -@popoverArrowOuterWidth; + margin-top: -@popoverArrowOuterWidth; + border-left-width: 0; + border-right-color: #999; // IE8 fallback + border-right-color: @popoverArrowOuterColor; + &:after { + left: 1px; + bottom: -@popoverArrowWidth; + border-left-width: 0; + border-right-color: @popoverArrowColor; + } + } + &.bottom .arrow { + left: 50%; + margin-left: -@popoverArrowOuterWidth; + border-top-width: 0; + border-bottom-color: #999; // IE8 fallback + border-bottom-color: @popoverArrowOuterColor; + top: -@popoverArrowOuterWidth; + &:after { + top: 1px; + margin-left: -@popoverArrowWidth; + border-top-width: 0; + border-bottom-color: @popoverArrowColor; + } + } + + &.left .arrow { + top: 50%; + right: -@popoverArrowOuterWidth; + margin-top: -@popoverArrowOuterWidth; + border-right-width: 0; + border-left-color: #999; // IE8 fallback + border-left-color: @popoverArrowOuterColor; + &:after { + right: 1px; + border-right-width: 0; + border-left-color: @popoverArrowColor; + bottom: -@popoverArrowWidth; + } + } + +} diff --git a/openid-connect-server-webapp/src/main/less/progress-bars.less b/openid-connect-server-webapp/src/main/less/progress-bars.less new file mode 100644 index 0000000000..5e0c3dda01 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/progress-bars.less @@ -0,0 +1,122 @@ +// +// Progress bars +// -------------------------------------------------- + + +// ANIMATIONS +// ---------- + +// Webkit +@-webkit-keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + +// Firefox +@-moz-keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + +// IE9 +@-ms-keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + +// Opera +@-o-keyframes progress-bar-stripes { + from { background-position: 0 0; } + to { background-position: 40px 0; } +} + +// Spec +@keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + + + +// THE BARS +// -------- + +// Outer container +.progress { + overflow: hidden; + height: @baseLineHeight; + margin-bottom: @baseLineHeight; + #gradient > .vertical(#f5f5f5, #f9f9f9); + .box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); + .border-radius(@baseBorderRadius); +} + +// Bar of progress +.progress .bar { + width: 0%; + height: 100%; + color: @white; + float: left; + font-size: 12px; + text-align: center; + text-shadow: 0 -1px 0 rgba(0,0,0,.25); + #gradient > .vertical(#149bdf, #0480be); + .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); + .box-sizing(border-box); + .transition(width .6s ease); +} +.progress .bar + .bar { + .box-shadow(~"inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15)"); +} + +// Striped bars +.progress-striped .bar { + #gradient > .striped(#149bdf); + .background-size(40px 40px); +} + +// Call animation for the active one +.progress.active .bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + -ms-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} + + + +// COLORS +// ------ + +// Danger (red) +.progress-danger .bar, .progress .bar-danger { + #gradient > .vertical(#ee5f5b, #c43c35); +} +.progress-danger.progress-striped .bar, .progress-striped .bar-danger { + #gradient > .striped(#ee5f5b); +} + +// Success (green) +.progress-success .bar, .progress .bar-success { + #gradient > .vertical(#62c462, #57a957); +} +.progress-success.progress-striped .bar, .progress-striped .bar-success { + #gradient > .striped(#62c462); +} + +// Info (teal) +.progress-info .bar, .progress .bar-info { + #gradient > .vertical(#5bc0de, #339bb9); +} +.progress-info.progress-striped .bar, .progress-striped .bar-info { + #gradient > .striped(#5bc0de); +} + +// Warning (orange) +.progress-warning .bar, .progress .bar-warning { + #gradient > .vertical(lighten(@orange, 15%), @orange); +} +.progress-warning.progress-striped .bar, .progress-striped .bar-warning { + #gradient > .striped(lighten(@orange, 15%)); +} diff --git a/openid-connect-server-webapp/src/main/less/reset.less b/openid-connect-server-webapp/src/main/less/reset.less new file mode 100644 index 0000000000..4806bd5e59 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/reset.less @@ -0,0 +1,216 @@ +// +// Reset CSS +// Adapted from http://github.com/necolas/normalize.css +// -------------------------------------------------- + + +// Display in IE6-9 and FF3 +// ------------------------- + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} + +// Display block in IE6-9 and FF3 +// ------------------------- + +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +// Prevents modern browsers from displaying 'audio' without controls +// ------------------------- + +audio:not([controls]) { + display: none; +} + +// Base settings +// ------------------------- + +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +// Focus states +a:focus { + .tab-focus(); +} +// Hover & Active +a:hover, +a:active { + outline: 0; +} + +// Prevents sub and sup affecting line-height in all browsers +// ------------------------- + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} + +// Img border in a's and image quality +// ------------------------- + +img { + /* Responsive images (ensure images don't scale beyond their parents) */ + max-width: 100%; /* Part 1: Set a maxium relative to the parent */ + width: auto\9; /* IE7-8 need help adjusting responsive images */ + height: auto; /* Part 2: Scale the height according to the width, otherwise you get stretching */ + + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; +} + +// Prevent max-width from affecting Google Maps +#map_canvas img, +.google-maps img { + max-width: none; +} + +// Forms +// ------------------------- + +// Font size in all browsers, margin changes, misc consistency +button, +input, +select, +textarea { + margin: 0; + font-size: 100%; + vertical-align: middle; +} +button, +input { + *overflow: visible; // Inner spacing ie IE6/7 + line-height: normal; // FF3/4 have !important on line-height in UA stylesheet +} +button::-moz-focus-inner, +input::-moz-focus-inner { // Inner padding and border oddities in FF3/4 + padding: 0; + border: 0; +} +button, +html input[type="button"], // Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; // Corrects inability to style clickable `input` types in iOS. + cursor: pointer; // Improves usability and consistency of cursor style between image-type `input` and others. +} +label, +select, +button, +input[type="button"], +input[type="reset"], +input[type="submit"], +input[type="radio"], +input[type="checkbox"] { + cursor: pointer; // Improves usability and consistency of cursor style between image-type `input` and others. +} +input[type="search"] { // Appearance in Safari/Chrome + .box-sizing(content-box); + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5 +} +textarea { + overflow: auto; // Remove vertical scrollbar in IE6-9 + vertical-align: top; // Readability and alignment cross-browser +} + + +// Printing +// ------------------------- +// Source: https://github.com/h5bp/html5-boilerplate/blob/master/css/main.css + +@media print { + + * { + text-shadow: none !important; + color: #000 !important; // Black prints faster: h5bp.com/s + background: transparent !important; + box-shadow: none !important; + } + + a, + a:visited { + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + // Don't show links for images, or javascript/internal links + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + + thead { + display: table-header-group; // h5bp.com/t + } + + tr, + img { + page-break-inside: avoid; + } + + img { + max-width: 100% !important; + } + + @page { + margin: 0.5cm; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } +} diff --git a/openid-connect-server-webapp/src/main/less/responsive-1200px-min.less b/openid-connect-server-webapp/src/main/less/responsive-1200px-min.less new file mode 100644 index 0000000000..4f35ba6ca2 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/responsive-1200px-min.less @@ -0,0 +1,28 @@ +// +// Responsive: Large desktop and up +// -------------------------------------------------- + + +@media (min-width: 1200px) { + + // Fixed grid + #grid > .core(@gridColumnWidth1200, @gridGutterWidth1200); + + // Fluid grid + #grid > .fluid(@fluidGridColumnWidth1200, @fluidGridGutterWidth1200); + + // Input grid + #grid > .input(@gridColumnWidth1200, @gridGutterWidth1200); + + // Thumbnails + .thumbnails { + margin-left: -@gridGutterWidth1200; + } + .thumbnails > li { + margin-left: @gridGutterWidth1200; + } + .row-fluid .thumbnails { + margin-left: 0; + } + +} diff --git a/openid-connect-server-webapp/src/main/less/responsive-767px-max.less b/openid-connect-server-webapp/src/main/less/responsive-767px-max.less new file mode 100644 index 0000000000..128f4ce30d --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/responsive-767px-max.less @@ -0,0 +1,193 @@ +// +// Responsive: Landscape phone to desktop/tablet +// -------------------------------------------------- + + +@media (max-width: 767px) { + + // Padding to set content in a bit + body { + padding-left: 20px; + padding-right: 20px; + } + // Negative indent the now static "fixed" navbar + .navbar-fixed-top, + .navbar-fixed-bottom, + .navbar-static-top { + margin-left: -20px; + margin-right: -20px; + } + // Remove padding on container given explicit padding set on body + .container-fluid { + padding: 0; + } + + // TYPOGRAPHY + // ---------- + // Reset horizontal dl + .dl-horizontal { + dt { + float: none; + clear: none; + width: auto; + text-align: left; + } + dd { + margin-left: 0; + } + } + + // GRID & CONTAINERS + // ----------------- + // Remove width from containers + .container { + width: auto; + } + // Fluid rows + .row-fluid { + width: 100%; + } + // Undo negative margin on rows and thumbnails + .row, + .thumbnails { + margin-left: 0; + } + .thumbnails > li { + float: none; + margin-left: 0; // Reset the default margin for all li elements when no .span* classes are present + } + // Make all grid-sized elements block level again + [class*="span"], + .uneditable-input[class*="span"], // Makes uneditable inputs full-width when using grid sizing + .row-fluid [class*="span"] { + float: none; + display: block; + width: 100%; + margin-left: 0; + .box-sizing(border-box); + } + .span12, + .row-fluid .span12 { + width: 100%; + .box-sizing(border-box); + } + .row-fluid [class*="offset"]:first-child { + margin-left: 0; + } + + // FORM FIELDS + // ----------- + // Make span* classes full width + .input-large, + .input-xlarge, + .input-xxlarge, + input[class*="span"], + select[class*="span"], + textarea[class*="span"], + .uneditable-input { + .input-block-level(); + } + // But don't let it screw up prepend/append inputs + .input-prepend input, + .input-append input, + .input-prepend input[class*="span"], + .input-append input[class*="span"] { + display: inline-block; // redeclare so they don't wrap to new lines + width: auto; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 0; + } + + // Modals + .modal { + position: fixed; + top: 20px; + left: 20px; + right: 20px; + width: auto; + margin: 0; + &.fade { top: -100px; } + &.fade.in { top: 20px; } + } + +} + + + +// UP TO LANDSCAPE PHONE +// --------------------- + +@media (max-width: 480px) { + + // Smooth out the collapsing/expanding nav + .nav-collapse { + -webkit-transform: translate3d(0, 0, 0); // activate the GPU + } + + // Block level the page header small tag for readability + .page-header h1 small { + display: block; + line-height: @baseLineHeight; + } + + // Update checkboxes for iOS + input[type="checkbox"], + input[type="radio"] { + border: 1px solid #ccc; + } + + // Remove the horizontal form styles + .form-horizontal { + .control-label { + float: none; + width: auto; + padding-top: 0; + text-align: left; + } + // Move over all input controls and content + .controls { + margin-left: 0; + } + // Move the options list down to align with labels + .control-list { + padding-top: 0; // has to be padding because margin collaspes + } + // Move over buttons in .form-actions to align with .controls + .form-actions { + padding-left: 10px; + padding-right: 10px; + } + } + + // Medias + // Reset float and spacing to stack + .media .pull-left, + .media .pull-right { + float: none; + display: block; + margin-bottom: 10px; + } + // Remove side margins since we stack instead of indent + .media-object { + margin-right: 0; + margin-left: 0; + } + + // Modals + .modal { + top: 10px; + left: 10px; + right: 10px; + } + .modal-header .close { + padding: 10px; + margin: -10px; + } + + // Carousel + .carousel-caption { + position: static; + } + +} diff --git a/openid-connect-server-webapp/src/main/less/responsive-768px-979px.less b/openid-connect-server-webapp/src/main/less/responsive-768px-979px.less new file mode 100644 index 0000000000..8e8c486a06 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/responsive-768px-979px.less @@ -0,0 +1,19 @@ +// +// Responsive: Tablet to desktop +// -------------------------------------------------- + + +@media (min-width: 768px) and (max-width: 979px) { + + // Fixed grid + #grid > .core(@gridColumnWidth768, @gridGutterWidth768); + + // Fluid grid + #grid > .fluid(@fluidGridColumnWidth768, @fluidGridGutterWidth768); + + // Input grid + #grid > .input(@gridColumnWidth768, @gridGutterWidth768); + + // No need to reset .thumbnails here since it's the same @gridGutterWidth + +} diff --git a/openid-connect-server-webapp/src/main/less/responsive-navbar.less b/openid-connect-server-webapp/src/main/less/responsive-navbar.less new file mode 100644 index 0000000000..21cd3ba671 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/responsive-navbar.less @@ -0,0 +1,189 @@ +// +// Responsive: Navbar +// -------------------------------------------------- + + +// TABLETS AND BELOW +// ----------------- +@media (max-width: @navbarCollapseWidth) { + + // UNFIX THE TOPBAR + // ---------------- + // Remove any padding from the body + body { + padding-top: 0; + } + // Unfix the navbars + .navbar-fixed-top, + .navbar-fixed-bottom { + position: static; + } + .navbar-fixed-top { + margin-bottom: @baseLineHeight; + } + .navbar-fixed-bottom { + margin-top: @baseLineHeight; + } + .navbar-fixed-top .navbar-inner, + .navbar-fixed-bottom .navbar-inner { + padding: 5px; + } + .navbar .container { + width: auto; + padding: 0; + } + // Account for brand name + .navbar .brand { + padding-left: 10px; + padding-right: 10px; + margin: 0 0 0 -5px; + } + + // COLLAPSIBLE NAVBAR + // ------------------ + // Nav collapse clears brand + .nav-collapse { + clear: both; + } + // Block-level the nav + .nav-collapse .nav { + float: none; + margin: 0 0 (@baseLineHeight / 2); + } + .nav-collapse .nav > li { + float: none; + } + .nav-collapse .nav > li > a { + margin-bottom: 2px; + } + .nav-collapse .nav > .divider-vertical { + display: none; + } + .nav-collapse .nav .nav-header { + color: @navbarText; + text-shadow: none; + } + // Nav and dropdown links in navbar + .nav-collapse .nav > li > a, + .nav-collapse .dropdown-menu a { + padding: 9px 15px; + font-weight: bold; + color: @navbarLinkColor; + .border-radius(3px); + } + // Buttons + .nav-collapse .btn { + padding: 4px 10px 4px; + font-weight: normal; + .border-radius(@baseBorderRadius); + } + .nav-collapse .dropdown-menu li + li a { + margin-bottom: 2px; + } + .nav-collapse .nav > li > a:hover, + .nav-collapse .nav > li > a:focus, + .nav-collapse .dropdown-menu a:hover, + .nav-collapse .dropdown-menu a:focus { + background-color: @navbarBackground; + } + .navbar-inverse .nav-collapse .nav > li > a, + .navbar-inverse .nav-collapse .dropdown-menu a { + color: @navbarInverseLinkColor; + } + .navbar-inverse .nav-collapse .nav > li > a:hover, + .navbar-inverse .nav-collapse .nav > li > a:focus, + .navbar-inverse .nav-collapse .dropdown-menu a:hover, + .navbar-inverse .nav-collapse .dropdown-menu a:focus { + background-color: @navbarInverseBackground; + } + // Buttons in the navbar + .nav-collapse.in .btn-group { + margin-top: 5px; + padding: 0; + } + // Dropdowns in the navbar + .nav-collapse .dropdown-menu { + position: static; + top: auto; + left: auto; + float: none; + display: none; + max-width: none; + margin: 0 15px; + padding: 0; + background-color: transparent; + border: none; + .border-radius(0); + .box-shadow(none); + } + .nav-collapse .open > .dropdown-menu { + display: block; + } + + .nav-collapse .dropdown-menu:before, + .nav-collapse .dropdown-menu:after { + display: none; + } + .nav-collapse .dropdown-menu .divider { + display: none; + } + .nav-collapse .nav > li > .dropdown-menu { + &:before, + &:after { + display: none; + } + } + // Forms in navbar + .nav-collapse .navbar-form, + .nav-collapse .navbar-search { + float: none; + padding: (@baseLineHeight / 2) 15px; + margin: (@baseLineHeight / 2) 0; + border-top: 1px solid @navbarBackground; + border-bottom: 1px solid @navbarBackground; + .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1)"); + } + .navbar-inverse .nav-collapse .navbar-form, + .navbar-inverse .nav-collapse .navbar-search { + border-top-color: @navbarInverseBackground; + border-bottom-color: @navbarInverseBackground; + } + // Pull right (secondary) nav content + .navbar .nav-collapse .nav.pull-right { + float: none; + margin-left: 0; + } + // Hide everything in the navbar save .brand and toggle button */ + .nav-collapse, + .nav-collapse.collapse { + overflow: hidden; + height: 0; + } + // Navbar button + .navbar .btn-navbar { + display: block; + } + + // STATIC NAVBAR + // ------------- + .navbar-static .navbar-inner { + padding-left: 10px; + padding-right: 10px; + } + + +} + + +// DEFAULT DESKTOP +// --------------- + +@media (min-width: @navbarCollapseDesktopWidth) { + + // Required to make the collapsing navbar work on regular desktops + .nav-collapse.collapse { + height: auto !important; + overflow: visible !important; + } + +} diff --git a/openid-connect-server-webapp/src/main/less/responsive-utilities.less b/openid-connect-server-webapp/src/main/less/responsive-utilities.less new file mode 100644 index 0000000000..bf43e8ef73 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/responsive-utilities.less @@ -0,0 +1,59 @@ +// +// Responsive: Utility classes +// -------------------------------------------------- + + +// IE10 Metro responsive +// Required for Windows 8 Metro split-screen snapping with IE10 +// Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/ +@-ms-viewport{ + width: device-width; +} + +// Hide from screenreaders and browsers +// Credit: HTML5 Boilerplate +.hidden { + display: none; + visibility: hidden; +} + +// Visibility utilities + +// For desktops +.visible-phone { display: none !important; } +.visible-tablet { display: none !important; } +.hidden-phone { } +.hidden-tablet { } +.hidden-desktop { display: none !important; } +.visible-desktop { display: inherit !important; } + +// Tablets & small desktops only +@media (min-width: 768px) and (max-width: 979px) { + // Hide everything else + .hidden-desktop { display: inherit !important; } + .visible-desktop { display: none !important ; } + // Show + .visible-tablet { display: inherit !important; } + // Hide + .hidden-tablet { display: none !important; } +} + +// Phones only +@media (max-width: 767px) { + // Hide everything else + .hidden-desktop { display: inherit !important; } + .visible-desktop { display: none !important; } + // Show + .visible-phone { display: inherit !important; } // Use inherit to restore previous behavior + // Hide + .hidden-phone { display: none !important; } +} + +// Print utilities +.visible-print { display: none !important; } +.hidden-print { } + +@media print { + .visible-print { display: inherit !important; } + .hidden-print { display: none !important; } +} diff --git a/openid-connect-server-webapp/src/main/less/scaffolding.less b/openid-connect-server-webapp/src/main/less/scaffolding.less new file mode 100644 index 0000000000..f17e8cadb4 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/scaffolding.less @@ -0,0 +1,53 @@ +// +// Scaffolding +// -------------------------------------------------- + + +// Body reset +// ------------------------- + +body { + margin: 0; + font-family: @baseFontFamily; + font-size: @baseFontSize; + line-height: @baseLineHeight; + color: @textColor; + background-color: @bodyBackground; +} + + +// Links +// ------------------------- + +a { + color: @linkColor; + text-decoration: none; +} +a:hover, +a:focus { + color: @linkColorHover; + text-decoration: underline; +} + + +// Images +// ------------------------- + +// Rounded corners +.img-rounded { + .border-radius(6px); +} + +// Add polaroid-esque trim +.img-polaroid { + padding: 4px; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0,0,0,.2); + .box-shadow(0 1px 3px rgba(0,0,0,.1)); +} + +// Perfect circle +.img-circle { + .border-radius(500px); // crank the border-radius so it works with most reasonably sized images +} diff --git a/openid-connect-server-webapp/src/main/less/sprites.less b/openid-connect-server-webapp/src/main/less/sprites.less new file mode 100644 index 0000000000..1812bf71ac --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/sprites.less @@ -0,0 +1,197 @@ +// +// Sprites +// -------------------------------------------------- + + +// ICONS +// ----- + +// All icons receive the styles of the tag with a base class +// of .i and are then given a unique class to add width, height, +// and background-position. Your resulting HTML will look like +// . + +// For the white version of the icons, just add the .icon-white class: +// + +[class^="icon-"], +[class*=" icon-"] { + display: inline-block; + width: 14px; + height: 14px; + .ie7-restore-right-whitespace(); + line-height: 14px; + vertical-align: text-top; + background-image: url("@{iconSpritePath}"); + background-position: 14px 14px; + background-repeat: no-repeat; + margin-top: 1px; +} + +/* White icons with optional class, or on hover/focus/active states of certain elements */ +.icon-white, +.nav-pills > .active > a > [class^="icon-"], +.nav-pills > .active > a > [class*=" icon-"], +.nav-list > .active > a > [class^="icon-"], +.nav-list > .active > a > [class*=" icon-"], +.navbar-inverse .nav > .active > a > [class^="icon-"], +.navbar-inverse .nav > .active > a > [class*=" icon-"], +.dropdown-menu > li > a:hover > [class^="icon-"], +.dropdown-menu > li > a:focus > [class^="icon-"], +.dropdown-menu > li > a:hover > [class*=" icon-"], +.dropdown-menu > li > a:focus > [class*=" icon-"], +.dropdown-menu > .active > a > [class^="icon-"], +.dropdown-menu > .active > a > [class*=" icon-"], +.dropdown-submenu:hover > a > [class^="icon-"], +.dropdown-submenu:focus > a > [class^="icon-"], +.dropdown-submenu:hover > a > [class*=" icon-"], +.dropdown-submenu:focus > a > [class*=" icon-"] { + background-image: url("@{iconWhiteSpritePath}"); +} + +.icon-glass { background-position: 0 0; } +.icon-music { background-position: -24px 0; } +.icon-search { background-position: -48px 0; } +.icon-envelope { background-position: -72px 0; } +.icon-heart { background-position: -96px 0; } +.icon-star { background-position: -120px 0; } +.icon-star-empty { background-position: -144px 0; } +.icon-user { background-position: -168px 0; } +.icon-film { background-position: -192px 0; } +.icon-th-large { background-position: -216px 0; } +.icon-th { background-position: -240px 0; } +.icon-th-list { background-position: -264px 0; } +.icon-ok { background-position: -288px 0; } +.icon-remove { background-position: -312px 0; } +.icon-zoom-in { background-position: -336px 0; } +.icon-zoom-out { background-position: -360px 0; } +.icon-off { background-position: -384px 0; } +.icon-signal { background-position: -408px 0; } +.icon-cog { background-position: -432px 0; } +.icon-trash { background-position: -456px 0; } + +.icon-home { background-position: 0 -24px; } +.icon-file { background-position: -24px -24px; } +.icon-time { background-position: -48px -24px; } +.icon-road { background-position: -72px -24px; } +.icon-download-alt { background-position: -96px -24px; } +.icon-download { background-position: -120px -24px; } +.icon-upload { background-position: -144px -24px; } +.icon-inbox { background-position: -168px -24px; } +.icon-play-circle { background-position: -192px -24px; } +.icon-repeat { background-position: -216px -24px; } +.icon-refresh { background-position: -240px -24px; } +.icon-list-alt { background-position: -264px -24px; } +.icon-lock { background-position: -287px -24px; } // 1px off +.icon-flag { background-position: -312px -24px; } +.icon-headphones { background-position: -336px -24px; } +.icon-volume-off { background-position: -360px -24px; } +.icon-volume-down { background-position: -384px -24px; } +.icon-volume-up { background-position: -408px -24px; } +.icon-qrcode { background-position: -432px -24px; } +.icon-barcode { background-position: -456px -24px; } + +.icon-tag { background-position: 0 -48px; } +.icon-tags { background-position: -25px -48px; } // 1px off +.icon-book { background-position: -48px -48px; } +.icon-bookmark { background-position: -72px -48px; } +.icon-print { background-position: -96px -48px; } +.icon-camera { background-position: -120px -48px; } +.icon-font { background-position: -144px -48px; } +.icon-bold { background-position: -167px -48px; } // 1px off +.icon-italic { background-position: -192px -48px; } +.icon-text-height { background-position: -216px -48px; } +.icon-text-width { background-position: -240px -48px; } +.icon-align-left { background-position: -264px -48px; } +.icon-align-center { background-position: -288px -48px; } +.icon-align-right { background-position: -312px -48px; } +.icon-align-justify { background-position: -336px -48px; } +.icon-list { background-position: -360px -48px; } +.icon-indent-left { background-position: -384px -48px; } +.icon-indent-right { background-position: -408px -48px; } +.icon-facetime-video { background-position: -432px -48px; } +.icon-picture { background-position: -456px -48px; } + +.icon-pencil { background-position: 0 -72px; } +.icon-map-marker { background-position: -24px -72px; } +.icon-adjust { background-position: -48px -72px; } +.icon-tint { background-position: -72px -72px; } +.icon-edit { background-position: -96px -72px; } +.icon-share { background-position: -120px -72px; } +.icon-check { background-position: -144px -72px; } +.icon-move { background-position: -168px -72px; } +.icon-step-backward { background-position: -192px -72px; } +.icon-fast-backward { background-position: -216px -72px; } +.icon-backward { background-position: -240px -72px; } +.icon-play { background-position: -264px -72px; } +.icon-pause { background-position: -288px -72px; } +.icon-stop { background-position: -312px -72px; } +.icon-forward { background-position: -336px -72px; } +.icon-fast-forward { background-position: -360px -72px; } +.icon-step-forward { background-position: -384px -72px; } +.icon-eject { background-position: -408px -72px; } +.icon-chevron-left { background-position: -432px -72px; } +.icon-chevron-right { background-position: -456px -72px; } + +.icon-plus-sign { background-position: 0 -96px; } +.icon-minus-sign { background-position: -24px -96px; } +.icon-remove-sign { background-position: -48px -96px; } +.icon-ok-sign { background-position: -72px -96px; } +.icon-question-sign { background-position: -96px -96px; } +.icon-info-sign { background-position: -120px -96px; } +.icon-screenshot { background-position: -144px -96px; } +.icon-remove-circle { background-position: -168px -96px; } +.icon-ok-circle { background-position: -192px -96px; } +.icon-ban-circle { background-position: -216px -96px; } +.icon-arrow-left { background-position: -240px -96px; } +.icon-arrow-right { background-position: -264px -96px; } +.icon-arrow-up { background-position: -289px -96px; } // 1px off +.icon-arrow-down { background-position: -312px -96px; } +.icon-share-alt { background-position: -336px -96px; } +.icon-resize-full { background-position: -360px -96px; } +.icon-resize-small { background-position: -384px -96px; } +.icon-plus { background-position: -408px -96px; } +.icon-minus { background-position: -433px -96px; } +.icon-asterisk { background-position: -456px -96px; } + +.icon-exclamation-sign { background-position: 0 -120px; } +.icon-gift { background-position: -24px -120px; } +.icon-leaf { background-position: -48px -120px; } +.icon-fire { background-position: -72px -120px; } +.icon-eye-open { background-position: -96px -120px; } +.icon-eye-close { background-position: -120px -120px; } +.icon-warning-sign { background-position: -144px -120px; } +.icon-plane { background-position: -168px -120px; } +.icon-calendar { background-position: -192px -120px; } +.icon-random { background-position: -216px -120px; width: 16px; } +.icon-comment { background-position: -240px -120px; } +.icon-magnet { background-position: -264px -120px; } +.icon-chevron-up { background-position: -288px -120px; } +.icon-chevron-down { background-position: -313px -119px; } // 1px, 1px off +.icon-retweet { background-position: -336px -120px; } +.icon-shopping-cart { background-position: -360px -120px; } +.icon-folder-close { background-position: -384px -120px; width: 16px; } +.icon-folder-open { background-position: -408px -120px; width: 16px; } +.icon-resize-vertical { background-position: -432px -119px; } // 1px, 1px off +.icon-resize-horizontal { background-position: -456px -118px; } // 1px, 2px off + +.icon-hdd { background-position: 0 -144px; } +.icon-bullhorn { background-position: -24px -144px; } +.icon-bell { background-position: -48px -144px; } +.icon-certificate { background-position: -72px -144px; } +.icon-thumbs-up { background-position: -96px -144px; } +.icon-thumbs-down { background-position: -120px -144px; } +.icon-hand-right { background-position: -144px -144px; } +.icon-hand-left { background-position: -168px -144px; } +.icon-hand-up { background-position: -192px -144px; } +.icon-hand-down { background-position: -216px -144px; } +.icon-circle-arrow-right { background-position: -240px -144px; } +.icon-circle-arrow-left { background-position: -264px -144px; } +.icon-circle-arrow-up { background-position: -288px -144px; } +.icon-circle-arrow-down { background-position: -312px -144px; } +.icon-globe { background-position: -336px -144px; } +.icon-wrench { background-position: -360px -144px; } +.icon-tasks { background-position: -384px -144px; } +.icon-filter { background-position: -408px -144px; } +.icon-briefcase { background-position: -432px -144px; } +.icon-fullscreen { background-position: -456px -144px; } diff --git a/openid-connect-server-webapp/src/main/less/tables.less b/openid-connect-server-webapp/src/main/less/tables.less new file mode 100644 index 0000000000..0e35271e11 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/tables.less @@ -0,0 +1,244 @@ +// +// Tables +// -------------------------------------------------- + + +// BASE TABLES +// ----------------- + +table { + max-width: 100%; + background-color: @tableBackground; + border-collapse: collapse; + border-spacing: 0; +} + +// BASELINE STYLES +// --------------- + +.table { + width: 100%; + margin-bottom: @baseLineHeight; + // Cells + th, + td { + padding: 8px; + line-height: @baseLineHeight; + text-align: left; + vertical-align: top; + border-top: 1px solid @tableBorder; + } + th { + font-weight: bold; + } + // Bottom align for column headings + thead th { + vertical-align: bottom; + } + // Remove top border from thead by default + caption + thead tr:first-child th, + caption + thead tr:first-child td, + colgroup + thead tr:first-child th, + colgroup + thead tr:first-child td, + thead:first-child tr:first-child th, + thead:first-child tr:first-child td { + border-top: 0; + } + // Account for multiple tbody instances + tbody + tbody { + border-top: 2px solid @tableBorder; + } + + // Nesting + .table { + background-color: @bodyBackground; + } +} + + + +// CONDENSED TABLE W/ HALF PADDING +// ------------------------------- + +.table-condensed { + th, + td { + padding: 4px 5px; + } +} + + +// BORDERED VERSION +// ---------------- + +.table-bordered { + border: 1px solid @tableBorder; + border-collapse: separate; // Done so we can round those corners! + *border-collapse: collapse; // IE7 can't round corners anyway + border-left: 0; + .border-radius(@baseBorderRadius); + th, + td { + border-left: 1px solid @tableBorder; + } + // Prevent a double border + caption + thead tr:first-child th, + caption + tbody tr:first-child th, + caption + tbody tr:first-child td, + colgroup + thead tr:first-child th, + colgroup + tbody tr:first-child th, + colgroup + tbody tr:first-child td, + thead:first-child tr:first-child th, + tbody:first-child tr:first-child th, + tbody:first-child tr:first-child td { + border-top: 0; + } + // For first th/td in the first row in the first thead or tbody + thead:first-child tr:first-child > th:first-child, + tbody:first-child tr:first-child > td:first-child, + tbody:first-child tr:first-child > th:first-child { + .border-top-left-radius(@baseBorderRadius); + } + // For last th/td in the first row in the first thead or tbody + thead:first-child tr:first-child > th:last-child, + tbody:first-child tr:first-child > td:last-child, + tbody:first-child tr:first-child > th:last-child { + .border-top-right-radius(@baseBorderRadius); + } + // For first th/td (can be either) in the last row in the last thead, tbody, and tfoot + thead:last-child tr:last-child > th:first-child, + tbody:last-child tr:last-child > td:first-child, + tbody:last-child tr:last-child > th:first-child, + tfoot:last-child tr:last-child > td:first-child, + tfoot:last-child tr:last-child > th:first-child { + .border-bottom-left-radius(@baseBorderRadius); + } + // For last th/td (can be either) in the last row in the last thead, tbody, and tfoot + thead:last-child tr:last-child > th:last-child, + tbody:last-child tr:last-child > td:last-child, + tbody:last-child tr:last-child > th:last-child, + tfoot:last-child tr:last-child > td:last-child, + tfoot:last-child tr:last-child > th:last-child { + .border-bottom-right-radius(@baseBorderRadius); + } + + // Clear border-radius for first and last td in the last row in the last tbody for table with tfoot + tfoot + tbody:last-child tr:last-child td:first-child { + .border-bottom-left-radius(0); + } + tfoot + tbody:last-child tr:last-child td:last-child { + .border-bottom-right-radius(0); + } + + // Special fixes to round the left border on the first td/th + caption + thead tr:first-child th:first-child, + caption + tbody tr:first-child td:first-child, + colgroup + thead tr:first-child th:first-child, + colgroup + tbody tr:first-child td:first-child { + .border-top-left-radius(@baseBorderRadius); + } + caption + thead tr:first-child th:last-child, + caption + tbody tr:first-child td:last-child, + colgroup + thead tr:first-child th:last-child, + colgroup + tbody tr:first-child td:last-child { + .border-top-right-radius(@baseBorderRadius); + } + +} + + + + +// ZEBRA-STRIPING +// -------------- + +// Default zebra-stripe styles (alternating gray and transparent backgrounds) +.table-striped { + tbody { + > tr:nth-child(odd) > td, + > tr:nth-child(odd) > th { + background-color: @tableBackgroundAccent; + } + } +} + + +// HOVER EFFECT +// ------------ +// Placed here since it has to come after the potential zebra striping +.table-hover { + tbody { + tr:hover > td, + tr:hover > th { + background-color: @tableBackgroundHover; + } + } +} + + +// TABLE CELL SIZING +// ----------------- + +// Reset default grid behavior +table td[class*="span"], +table th[class*="span"], +.row-fluid table td[class*="span"], +.row-fluid table th[class*="span"] { + display: table-cell; + float: none; // undo default grid column styles + margin-left: 0; // undo default grid column styles +} + +// Change the column widths to account for td/th padding +.table td, +.table th { + &.span1 { .tableColumns(1); } + &.span2 { .tableColumns(2); } + &.span3 { .tableColumns(3); } + &.span4 { .tableColumns(4); } + &.span5 { .tableColumns(5); } + &.span6 { .tableColumns(6); } + &.span7 { .tableColumns(7); } + &.span8 { .tableColumns(8); } + &.span9 { .tableColumns(9); } + &.span10 { .tableColumns(10); } + &.span11 { .tableColumns(11); } + &.span12 { .tableColumns(12); } +} + + + +// TABLE BACKGROUNDS +// ----------------- +// Exact selectors below required to override .table-striped + +.table tbody tr { + &.success > td { + background-color: @successBackground; + } + &.error > td { + background-color: @errorBackground; + } + &.warning > td { + background-color: @warningBackground; + } + &.info > td { + background-color: @infoBackground; + } +} + +// Hover states for .table-hover +.table-hover tbody tr { + &.success:hover > td { + background-color: darken(@successBackground, 5%); + } + &.error:hover > td { + background-color: darken(@errorBackground, 5%); + } + &.warning:hover > td { + background-color: darken(@warningBackground, 5%); + } + &.info:hover > td { + background-color: darken(@infoBackground, 5%); + } +} diff --git a/openid-connect-server-webapp/src/main/less/thumbnails.less b/openid-connect-server-webapp/src/main/less/thumbnails.less new file mode 100644 index 0000000000..4fd07d2533 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/thumbnails.less @@ -0,0 +1,53 @@ +// +// Thumbnails +// -------------------------------------------------- + + +// Note: `.thumbnails` and `.thumbnails > li` are overriden in responsive files + +// Make wrapper ul behave like the grid +.thumbnails { + margin-left: -@gridGutterWidth; + list-style: none; + .clearfix(); +} +// Fluid rows have no left margin +.row-fluid .thumbnails { + margin-left: 0; +} + +// Float li to make thumbnails appear in a row +.thumbnails > li { + float: left; // Explicity set the float since we don't require .span* classes + margin-bottom: @baseLineHeight; + margin-left: @gridGutterWidth; +} + +// The actual thumbnail (can be `a` or `div`) +.thumbnail { + display: block; + padding: 4px; + line-height: @baseLineHeight; + border: 1px solid #ddd; + .border-radius(@baseBorderRadius); + .box-shadow(0 1px 3px rgba(0,0,0,.055)); + .transition(all .2s ease-in-out); +} +// Add a hover/focus state for linked versions only +a.thumbnail:hover, +a.thumbnail:focus { + border-color: @linkColor; + .box-shadow(0 1px 4px rgba(0,105,214,.25)); +} + +// Images and captions +.thumbnail > img { + display: block; + max-width: 100%; + margin-left: auto; + margin-right: auto; +} +.thumbnail .caption { + padding: 9px; + color: @gray; +} diff --git a/openid-connect-server-webapp/src/main/less/tooltip.less b/openid-connect-server-webapp/src/main/less/tooltip.less new file mode 100644 index 0000000000..83d5f2bd76 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/tooltip.less @@ -0,0 +1,70 @@ +// +// Tooltips +// -------------------------------------------------- + + +// Base class +.tooltip { + position: absolute; + z-index: @zindexTooltip; + display: block; + visibility: visible; + font-size: 11px; + line-height: 1.4; + .opacity(0); + &.in { .opacity(80); } + &.top { margin-top: -3px; padding: 5px 0; } + &.right { margin-left: 3px; padding: 0 5px; } + &.bottom { margin-top: 3px; padding: 5px 0; } + &.left { margin-left: -3px; padding: 0 5px; } +} + +// Wrapper for the tooltip content +.tooltip-inner { + max-width: 200px; + padding: 8px; + color: @tooltipColor; + text-align: center; + text-decoration: none; + background-color: @tooltipBackground; + .border-radius(@baseBorderRadius); +} + +// Arrows +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip { + &.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -@tooltipArrowWidth; + border-width: @tooltipArrowWidth @tooltipArrowWidth 0; + border-top-color: @tooltipArrowColor; + } + &.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -@tooltipArrowWidth; + border-width: @tooltipArrowWidth @tooltipArrowWidth @tooltipArrowWidth 0; + border-right-color: @tooltipArrowColor; + } + &.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -@tooltipArrowWidth; + border-width: @tooltipArrowWidth 0 @tooltipArrowWidth @tooltipArrowWidth; + border-left-color: @tooltipArrowColor; + } + &.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -@tooltipArrowWidth; + border-width: 0 @tooltipArrowWidth @tooltipArrowWidth; + border-bottom-color: @tooltipArrowColor; + } +} diff --git a/openid-connect-server-webapp/src/main/less/type.less b/openid-connect-server-webapp/src/main/less/type.less new file mode 100644 index 0000000000..6a472db497 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/type.less @@ -0,0 +1,247 @@ +// +// Typography +// -------------------------------------------------- + + +// Body text +// ------------------------- + +p { + margin: 0 0 @baseLineHeight / 2; +} +.lead { + margin-bottom: @baseLineHeight; + font-size: @baseFontSize * 1.5; + font-weight: 200; + line-height: @baseLineHeight * 1.5; +} + + +// Emphasis & misc +// ------------------------- + +// Ex: 14px base font * 85% = about 12px +small { font-size: 85%; } + +strong { font-weight: bold; } +em { font-style: italic; } +cite { font-style: normal; } + +// Utility classes +.muted { color: @grayLight; } +a.muted:hover, +a.muted:focus { color: darken(@grayLight, 10%); } + +.text-warning { color: @warningText; } +a.text-warning:hover, +a.text-warning:focus { color: darken(@warningText, 10%); } + +.text-error { color: @errorText; } +a.text-error:hover, +a.text-error:focus { color: darken(@errorText, 10%); } + +.text-info { color: @infoText; } +a.text-info:hover, +a.text-info:focus { color: darken(@infoText, 10%); } + +.text-success { color: @successText; } +a.text-success:hover, +a.text-success:focus { color: darken(@successText, 10%); } + +.text-left { text-align: left; } +.text-right { text-align: right; } +.text-center { text-align: center; } + + +// Headings +// ------------------------- + +h1, h2, h3, h4, h5, h6 { + margin: (@baseLineHeight / 2) 0; + font-family: @headingsFontFamily; + font-weight: @headingsFontWeight; + line-height: @baseLineHeight; + color: @headingsColor; + text-rendering: optimizelegibility; // Fix the character spacing for headings + small { + font-weight: normal; + line-height: 1; + color: @grayLight; + } +} + +h1, +h2, +h3 { line-height: @baseLineHeight * 2; } + +h1 { font-size: @baseFontSize * 2.75; } // ~38px +h2 { font-size: @baseFontSize * 2.25; } // ~32px +h3 { font-size: @baseFontSize * 1.75; } // ~24px +h4 { font-size: @baseFontSize * 1.25; } // ~18px +h5 { font-size: @baseFontSize; } +h6 { font-size: @baseFontSize * 0.85; } // ~12px + +h1 small { font-size: @baseFontSize * 1.75; } // ~24px +h2 small { font-size: @baseFontSize * 1.25; } // ~18px +h3 small { font-size: @baseFontSize; } +h4 small { font-size: @baseFontSize; } + + +// Page header +// ------------------------- + +.page-header { + padding-bottom: (@baseLineHeight / 2) - 1; + margin: @baseLineHeight 0 (@baseLineHeight * 1.5); + border-bottom: 1px solid @grayLighter; +} + + + +// Lists +// -------------------------------------------------- + +// Unordered and Ordered lists +ul, ol { + padding: 0; + margin: 0 0 @baseLineHeight / 2 25px; +} +ul ul, +ul ol, +ol ol, +ol ul { + margin-bottom: 0; +} +li { + line-height: @baseLineHeight; +} + +// Remove default list styles +ul.unstyled, +ol.unstyled { + margin-left: 0; + list-style: none; +} + +// Single-line list items +ul.inline, +ol.inline { + margin-left: 0; + list-style: none; + > li { + display: inline-block; + .ie7-inline-block(); + padding-left: 5px; + padding-right: 5px; + } +} + +// Description Lists +dl { + margin-bottom: @baseLineHeight; +} +dt, +dd { + line-height: @baseLineHeight; +} +dt { + font-weight: bold; +} +dd { + margin-left: @baseLineHeight / 2; +} +// Horizontal layout (like forms) +.dl-horizontal { + .clearfix(); // Ensure dl clears floats if empty dd elements present + dt { + float: left; + width: @horizontalComponentOffset - 20; + clear: left; + text-align: right; + .text-overflow(); + } + dd { + margin-left: @horizontalComponentOffset; + } +} + +// MISC +// ---- + +// Horizontal rules +hr { + margin: @baseLineHeight 0; + border: 0; + border-top: 1px solid @hrBorder; + border-bottom: 1px solid @white; +} + +// Abbreviations and acronyms +abbr[title], +// Added data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257 +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted @grayLight; +} +abbr.initialism { + font-size: 90%; + text-transform: uppercase; +} + +// Blockquotes +blockquote { + padding: 0 0 0 15px; + margin: 0 0 @baseLineHeight; + border-left: 5px solid @grayLighter; + p { + margin-bottom: 0; + font-size: @baseFontSize * 1.25; + font-weight: 300; + line-height: 1.25; + } + small { + display: block; + line-height: @baseLineHeight; + color: @grayLight; + &:before { + content: '\2014 \00A0'; + } + } + + // Float right with text-align: right + &.pull-right { + float: right; + padding-right: 15px; + padding-left: 0; + border-right: 5px solid @grayLighter; + border-left: 0; + p, + small { + text-align: right; + } + small { + &:before { + content: ''; + } + &:after { + content: '\00A0 \2014'; + } + } + } +} + +// Quotes +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; +} + +// Addresses +address { + display: block; + margin-bottom: @baseLineHeight; + font-style: normal; + line-height: @baseLineHeight; +} diff --git a/openid-connect-server-webapp/src/main/less/utilities.less b/openid-connect-server-webapp/src/main/less/utilities.less new file mode 100644 index 0000000000..314b4ffdb4 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/utilities.less @@ -0,0 +1,30 @@ +// +// Utility classes +// -------------------------------------------------- + + +// Quick floats +.pull-right { + float: right; +} +.pull-left { + float: left; +} + +// Toggling content +.hide { + display: none; +} +.show { + display: block; +} + +// Visibility +.invisible { + visibility: hidden; +} + +// For Affix plugin +.affix { + position: fixed; +} diff --git a/openid-connect-server-webapp/src/main/less/variables.less b/openid-connect-server-webapp/src/main/less/variables.less new file mode 100644 index 0000000000..31c131b1e2 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/variables.less @@ -0,0 +1,301 @@ +// +// Variables +// -------------------------------------------------- + + +// Global values +// -------------------------------------------------- + + +// Grays +// ------------------------- +@black: #000; +@grayDarker: #222; +@grayDark: #333; +@gray: #555; +@grayLight: #999; +@grayLighter: #eee; +@white: #fff; + + +// Accent colors +// ------------------------- +@blue: #049cdb; +@blueDark: #0064cd; +@green: #46a546; +@red: #9d261d; +@yellow: #ffc40d; +@orange: #f89406; +@pink: #c3325f; +@purple: #7a43b6; + + +// Scaffolding +// ------------------------- +@bodyBackground: @white; +@textColor: @grayDark; + + +// Links +// ------------------------- +@linkColor: #08c; +@linkColorHover: darken(@linkColor, 15%); + + +// Typography +// ------------------------- +@sansFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif; +@serifFontFamily: Georgia, "Times New Roman", Times, serif; +@monoFontFamily: Monaco, Menlo, Consolas, "Courier New", monospace; + +@baseFontSize: 14px; +@baseFontFamily: @sansFontFamily; +@baseLineHeight: 20px; +@altFontFamily: @serifFontFamily; + +@headingsFontFamily: inherit; // empty to use BS default, @baseFontFamily +@headingsFontWeight: bold; // instead of browser default, bold +@headingsColor: inherit; // empty to use BS default, @textColor + + +// Component sizing +// ------------------------- +// Based on 14px font-size and 20px line-height + +@fontSizeLarge: @baseFontSize * 1.25; // ~18px +@fontSizeSmall: @baseFontSize * 0.85; // ~12px +@fontSizeMini: @baseFontSize * 0.75; // ~11px + +@paddingLarge: 11px 19px; // 44px +@paddingSmall: 2px 10px; // 26px +@paddingMini: 0 6px; // 22px + +@baseBorderRadius: 4px; +@borderRadiusLarge: 6px; +@borderRadiusSmall: 3px; + + +// Tables +// ------------------------- +@tableBackground: transparent; // overall background-color +@tableBackgroundAccent: #f9f9f9; // for striping +@tableBackgroundHover: #f5f5f5; // for hover +@tableBorder: #ddd; // table and cell border + +// Buttons +// ------------------------- +@btnBackground: @white; +@btnBackgroundHighlight: darken(@white, 10%); +@btnBorder: #ccc; + +@btnPrimaryBackground: @linkColor; +@btnPrimaryBackgroundHighlight: spin(@btnPrimaryBackground, 20%); + +@btnInfoBackground: #5bc0de; +@btnInfoBackgroundHighlight: #2f96b4; + +@btnSuccessBackground: #62c462; +@btnSuccessBackgroundHighlight: #51a351; + +@btnWarningBackground: lighten(@orange, 15%); +@btnWarningBackgroundHighlight: @orange; + +@btnDangerBackground: #ee5f5b; +@btnDangerBackgroundHighlight: #bd362f; + +@btnInverseBackground: #444; +@btnInverseBackgroundHighlight: @grayDarker; + + +// Forms +// ------------------------- +@inputBackground: @white; +@inputBorder: #ccc; +@inputBorderRadius: @baseBorderRadius; +@inputDisabledBackground: @grayLighter; +@formActionsBackground: #f5f5f5; +@inputHeight: @baseLineHeight + 10px; // base line-height + 8px vertical padding + 2px top/bottom border + + +// Dropdowns +// ------------------------- +@dropdownBackground: @white; +@dropdownBorder: rgba(0,0,0,.2); +@dropdownDividerTop: #e5e5e5; +@dropdownDividerBottom: @white; + +@dropdownLinkColor: @grayDark; +@dropdownLinkColorHover: @white; +@dropdownLinkColorActive: @white; + +@dropdownLinkBackgroundActive: @linkColor; +@dropdownLinkBackgroundHover: @dropdownLinkBackgroundActive; + + + +// COMPONENT VARIABLES +// -------------------------------------------------- + + +// Z-index master list +// ------------------------- +// Used for a bird's eye view of components dependent on the z-axis +// Try to avoid customizing these :) +@zindexDropdown: 1000; +@zindexPopover: 1010; +@zindexTooltip: 1030; +@zindexFixedNavbar: 1030; +@zindexModalBackdrop: 1040; +@zindexModal: 1050; + + +// Sprite icons path +// ------------------------- +@iconSpritePath: "../img/glyphicons-halflings.png"; +@iconWhiteSpritePath: "../img/glyphicons-halflings-white.png"; + + +// Input placeholder text color +// ------------------------- +@placeholderText: @grayLight; + + +// Hr border color +// ------------------------- +@hrBorder: @grayLighter; + + +// Horizontal forms & lists +// ------------------------- +@horizontalComponentOffset: 180px; + + +// Wells +// ------------------------- +@wellBackground: #f5f5f5; + + +// Navbar +// ------------------------- +@navbarCollapseWidth: 979px; +@navbarCollapseDesktopWidth: @navbarCollapseWidth + 1; + +@navbarHeight: 40px; +@navbarBackgroundHighlight: #ffffff; +@navbarBackground: darken(@navbarBackgroundHighlight, 5%); +@navbarBorder: darken(@navbarBackground, 12%); + +@navbarText: #777; +@navbarLinkColor: #777; +@navbarLinkColorHover: @grayDark; +@navbarLinkColorActive: @gray; +@navbarLinkBackgroundHover: transparent; +@navbarLinkBackgroundActive: darken(@navbarBackground, 5%); + +@navbarBrandColor: @navbarLinkColor; + +// Inverted navbar +@navbarInverseBackground: #111111; +@navbarInverseBackgroundHighlight: #222222; +@navbarInverseBorder: #252525; + +@navbarInverseText: @grayLight; +@navbarInverseLinkColor: @grayLight; +@navbarInverseLinkColorHover: @white; +@navbarInverseLinkColorActive: @navbarInverseLinkColorHover; +@navbarInverseLinkBackgroundHover: transparent; +@navbarInverseLinkBackgroundActive: @navbarInverseBackground; + +@navbarInverseSearchBackground: lighten(@navbarInverseBackground, 25%); +@navbarInverseSearchBackgroundFocus: @white; +@navbarInverseSearchBorder: @navbarInverseBackground; +@navbarInverseSearchPlaceholderColor: #ccc; + +@navbarInverseBrandColor: @navbarInverseLinkColor; + + +// Pagination +// ------------------------- +@paginationBackground: #fff; +@paginationBorder: #ddd; +@paginationActiveBackground: #f5f5f5; + + +// Hero unit +// ------------------------- +@heroUnitBackground: @grayLighter; +@heroUnitHeadingColor: inherit; +@heroUnitLeadColor: inherit; + + +// Form states and alerts +// ------------------------- +@warningText: #c09853; +@warningBackground: #fcf8e3; +@warningBorder: darken(spin(@warningBackground, -10), 3%); + +@errorText: #b94a48; +@errorBackground: #f2dede; +@errorBorder: darken(spin(@errorBackground, -10), 3%); + +@successText: #468847; +@successBackground: #dff0d8; +@successBorder: darken(spin(@successBackground, -10), 5%); + +@infoText: #3a87ad; +@infoBackground: #d9edf7; +@infoBorder: darken(spin(@infoBackground, -10), 7%); + + +// Tooltips and popovers +// ------------------------- +@tooltipColor: #fff; +@tooltipBackground: #000; +@tooltipArrowWidth: 5px; +@tooltipArrowColor: @tooltipBackground; + +@popoverBackground: #fff; +@popoverArrowWidth: 10px; +@popoverArrowColor: #fff; +@popoverTitleBackground: darken(@popoverBackground, 3%); + +// Special enhancement for popovers +@popoverArrowOuterWidth: @popoverArrowWidth + 1; +@popoverArrowOuterColor: rgba(0,0,0,.25); + + + +// GRID +// -------------------------------------------------- + + +// Default 940px grid +// ------------------------- +@gridColumns: 12; +@gridColumnWidth: 60px; +@gridGutterWidth: 20px; +@gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1)); + +// 1200px min +@gridColumnWidth1200: 70px; +@gridGutterWidth1200: 30px; +@gridRowWidth1200: (@gridColumns * @gridColumnWidth1200) + (@gridGutterWidth1200 * (@gridColumns - 1)); + +// 768px-979px +@gridColumnWidth768: 42px; +@gridGutterWidth768: 20px; +@gridRowWidth768: (@gridColumns * @gridColumnWidth768) + (@gridGutterWidth768 * (@gridColumns - 1)); + + +// Fluid grid +// ------------------------- +@fluidGridColumnWidth: percentage(@gridColumnWidth/@gridRowWidth); +@fluidGridGutterWidth: percentage(@gridGutterWidth/@gridRowWidth); + +// 1200px min +@fluidGridColumnWidth1200: percentage(@gridColumnWidth1200/@gridRowWidth1200); +@fluidGridGutterWidth1200: percentage(@gridGutterWidth1200/@gridRowWidth1200); + +// 768px-979px +@fluidGridColumnWidth768: percentage(@gridColumnWidth768/@gridRowWidth768); +@fluidGridGutterWidth768: percentage(@gridGutterWidth768/@gridRowWidth768); diff --git a/openid-connect-server-webapp/src/main/less/wells.less b/openid-connect-server-webapp/src/main/less/wells.less new file mode 100644 index 0000000000..84a744b1c5 --- /dev/null +++ b/openid-connect-server-webapp/src/main/less/wells.less @@ -0,0 +1,29 @@ +// +// Wells +// -------------------------------------------------- + + +// Base class +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: @wellBackground; + border: 1px solid darken(@wellBackground, 7%); + .border-radius(@baseBorderRadius); + .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); + blockquote { + border-color: #ddd; + border-color: rgba(0,0,0,.15); + } +} + +// Sizes +.well-large { + padding: 24px; + .border-radius(@borderRadiusLarge); +} +.well-small { + padding: 9px; + .border-radius(@borderRadiusSmall); +} diff --git a/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.css b/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.css deleted file mode 100644 index c0bba15b36..0000000000 --- a/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.css +++ /dev/null @@ -1,1109 +0,0 @@ -/*! - * Bootstrap Responsive v2.3.2 - * - * Copyright 2013 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world by @mdo and @fat. - */ - -.clearfix { - *zoom: 1; -} - -.clearfix:before, -.clearfix:after { - display: table; - line-height: 0; - content: ""; -} - -.clearfix:after { - clear: both; -} - -.hide-text { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} - -.input-block-level { - display: block; - width: 100%; - min-height: 30px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -@-ms-viewport { - width: device-width; -} - -.hidden { - display: none; - visibility: hidden; -} - -.visible-phone { - display: none !important; -} - -.visible-tablet { - display: none !important; -} - -.hidden-desktop { - display: none !important; -} - -.visible-desktop { - display: inherit !important; -} - -@media (min-width: 768px) and (max-width: 979px) { - .hidden-desktop { - display: inherit !important; - } - .visible-desktop { - display: none !important ; - } - .visible-tablet { - display: inherit !important; - } - .hidden-tablet { - display: none !important; - } -} - -@media (max-width: 767px) { - .hidden-desktop { - display: inherit !important; - } - .visible-desktop { - display: none !important; - } - .visible-phone { - display: inherit !important; - } - .hidden-phone { - display: none !important; - } -} - -.visible-print { - display: none !important; -} - -@media print { - .visible-print { - display: inherit !important; - } - .hidden-print { - display: none !important; - } -} - -@media (min-width: 1200px) { - .row { - margin-left: -30px; - *zoom: 1; - } - .row:before, - .row:after { - display: table; - line-height: 0; - content: ""; - } - .row:after { - clear: both; - } - [class*="span"] { - float: left; - min-height: 1px; - margin-left: 30px; - } - .container, - .navbar-static-top .container, - .navbar-fixed-top .container, - .navbar-fixed-bottom .container { - width: 1170px; - } - .span12 { - width: 1170px; - } - .span11 { - width: 1070px; - } - .span10 { - width: 970px; - } - .span9 { - width: 870px; - } - .span8 { - width: 770px; - } - .span7 { - width: 670px; - } - .span6 { - width: 570px; - } - .span5 { - width: 470px; - } - .span4 { - width: 370px; - } - .span3 { - width: 270px; - } - .span2 { - width: 170px; - } - .span1 { - width: 70px; - } - .offset12 { - margin-left: 1230px; - } - .offset11 { - margin-left: 1130px; - } - .offset10 { - margin-left: 1030px; - } - .offset9 { - margin-left: 930px; - } - .offset8 { - margin-left: 830px; - } - .offset7 { - margin-left: 730px; - } - .offset6 { - margin-left: 630px; - } - .offset5 { - margin-left: 530px; - } - .offset4 { - margin-left: 430px; - } - .offset3 { - margin-left: 330px; - } - .offset2 { - margin-left: 230px; - } - .offset1 { - margin-left: 130px; - } - .row-fluid { - width: 100%; - *zoom: 1; - } - .row-fluid:before, - .row-fluid:after { - display: table; - line-height: 0; - content: ""; - } - .row-fluid:after { - clear: both; - } - .row-fluid [class*="span"] { - display: block; - float: left; - width: 100%; - min-height: 30px; - margin-left: 2.564102564102564%; - *margin-left: 2.5109110747408616%; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - } - .row-fluid [class*="span"]:first-child { - margin-left: 0; - } - .row-fluid .controls-row [class*="span"] + [class*="span"] { - margin-left: 2.564102564102564%; - } - .row-fluid .span12 { - width: 100%; - *width: 99.94680851063829%; - } - .row-fluid .span11 { - width: 91.45299145299145%; - *width: 91.39979996362975%; - } - .row-fluid .span10 { - width: 82.90598290598291%; - *width: 82.8527914166212%; - } - .row-fluid .span9 { - width: 74.35897435897436%; - *width: 74.30578286961266%; - } - .row-fluid .span8 { - width: 65.81196581196582%; - *width: 65.75877432260411%; - } - .row-fluid .span7 { - width: 57.26495726495726%; - *width: 57.21176577559556%; - } - .row-fluid .span6 { - width: 48.717948717948715%; - *width: 48.664757228587014%; - } - .row-fluid .span5 { - width: 40.17094017094017%; - *width: 40.11774868157847%; - } - .row-fluid .span4 { - width: 31.623931623931625%; - *width: 31.570740134569924%; - } - .row-fluid .span3 { - width: 23.076923076923077%; - *width: 23.023731587561375%; - } - .row-fluid .span2 { - width: 14.52991452991453%; - *width: 14.476723040552828%; - } - .row-fluid .span1 { - width: 5.982905982905983%; - *width: 5.929714493544281%; - } - .row-fluid .offset12 { - margin-left: 105.12820512820512%; - *margin-left: 105.02182214948171%; - } - .row-fluid .offset12:first-child { - margin-left: 102.56410256410257%; - *margin-left: 102.45771958537915%; - } - .row-fluid .offset11 { - margin-left: 96.58119658119658%; - *margin-left: 96.47481360247316%; - } - .row-fluid .offset11:first-child { - margin-left: 94.01709401709402%; - *margin-left: 93.91071103837061%; - } - .row-fluid .offset10 { - margin-left: 88.03418803418803%; - *margin-left: 87.92780505546462%; - } - .row-fluid .offset10:first-child { - margin-left: 85.47008547008548%; - *margin-left: 85.36370249136206%; - } - .row-fluid .offset9 { - margin-left: 79.48717948717949%; - *margin-left: 79.38079650845607%; - } - .row-fluid .offset9:first-child { - margin-left: 76.92307692307693%; - *margin-left: 76.81669394435352%; - } - .row-fluid .offset8 { - margin-left: 70.94017094017094%; - *margin-left: 70.83378796144753%; - } - .row-fluid .offset8:first-child { - margin-left: 68.37606837606839%; - *margin-left: 68.26968539734497%; - } - .row-fluid .offset7 { - margin-left: 62.393162393162385%; - *margin-left: 62.28677941443899%; - } - .row-fluid .offset7:first-child { - margin-left: 59.82905982905982%; - *margin-left: 59.72267685033642%; - } - .row-fluid .offset6 { - margin-left: 53.84615384615384%; - *margin-left: 53.739770867430444%; - } - .row-fluid .offset6:first-child { - margin-left: 51.28205128205128%; - *margin-left: 51.175668303327875%; - } - .row-fluid .offset5 { - margin-left: 45.299145299145295%; - *margin-left: 45.1927623204219%; - } - .row-fluid .offset5:first-child { - margin-left: 42.73504273504273%; - *margin-left: 42.62865975631933%; - } - .row-fluid .offset4 { - margin-left: 36.75213675213675%; - *margin-left: 36.645753773413354%; - } - .row-fluid .offset4:first-child { - margin-left: 34.18803418803419%; - *margin-left: 34.081651209310785%; - } - .row-fluid .offset3 { - margin-left: 28.205128205128204%; - *margin-left: 28.0987452264048%; - } - .row-fluid .offset3:first-child { - margin-left: 25.641025641025642%; - *margin-left: 25.53464266230224%; - } - .row-fluid .offset2 { - margin-left: 19.65811965811966%; - *margin-left: 19.551736679396257%; - } - .row-fluid .offset2:first-child { - margin-left: 17.094017094017094%; - *margin-left: 16.98763411529369%; - } - .row-fluid .offset1 { - margin-left: 11.11111111111111%; - *margin-left: 11.004728132387708%; - } - .row-fluid .offset1:first-child { - margin-left: 8.547008547008547%; - *margin-left: 8.440625568285142%; - } - input, - textarea, - .uneditable-input { - margin-left: 0; - } - .controls-row [class*="span"] + [class*="span"] { - margin-left: 30px; - } - input.span12, - textarea.span12, - .uneditable-input.span12 { - width: 1156px; - } - input.span11, - textarea.span11, - .uneditable-input.span11 { - width: 1056px; - } - input.span10, - textarea.span10, - .uneditable-input.span10 { - width: 956px; - } - input.span9, - textarea.span9, - .uneditable-input.span9 { - width: 856px; - } - input.span8, - textarea.span8, - .uneditable-input.span8 { - width: 756px; - } - input.span7, - textarea.span7, - .uneditable-input.span7 { - width: 656px; - } - input.span6, - textarea.span6, - .uneditable-input.span6 { - width: 556px; - } - input.span5, - textarea.span5, - .uneditable-input.span5 { - width: 456px; - } - input.span4, - textarea.span4, - .uneditable-input.span4 { - width: 356px; - } - input.span3, - textarea.span3, - .uneditable-input.span3 { - width: 256px; - } - input.span2, - textarea.span2, - .uneditable-input.span2 { - width: 156px; - } - input.span1, - textarea.span1, - .uneditable-input.span1 { - width: 56px; - } - .thumbnails { - margin-left: -30px; - } - .thumbnails > li { - margin-left: 30px; - } - .row-fluid .thumbnails { - margin-left: 0; - } -} - -@media (min-width: 768px) and (max-width: 979px) { - .row { - margin-left: -20px; - *zoom: 1; - } - .row:before, - .row:after { - display: table; - line-height: 0; - content: ""; - } - .row:after { - clear: both; - } - [class*="span"] { - float: left; - min-height: 1px; - margin-left: 20px; - } - .container, - .navbar-static-top .container, - .navbar-fixed-top .container, - .navbar-fixed-bottom .container { - width: 724px; - } - .span12 { - width: 724px; - } - .span11 { - width: 662px; - } - .span10 { - width: 600px; - } - .span9 { - width: 538px; - } - .span8 { - width: 476px; - } - .span7 { - width: 414px; - } - .span6 { - width: 352px; - } - .span5 { - width: 290px; - } - .span4 { - width: 228px; - } - .span3 { - width: 166px; - } - .span2 { - width: 104px; - } - .span1 { - width: 42px; - } - .offset12 { - margin-left: 764px; - } - .offset11 { - margin-left: 702px; - } - .offset10 { - margin-left: 640px; - } - .offset9 { - margin-left: 578px; - } - .offset8 { - margin-left: 516px; - } - .offset7 { - margin-left: 454px; - } - .offset6 { - margin-left: 392px; - } - .offset5 { - margin-left: 330px; - } - .offset4 { - margin-left: 268px; - } - .offset3 { - margin-left: 206px; - } - .offset2 { - margin-left: 144px; - } - .offset1 { - margin-left: 82px; - } - .row-fluid { - width: 100%; - *zoom: 1; - } - .row-fluid:before, - .row-fluid:after { - display: table; - line-height: 0; - content: ""; - } - .row-fluid:after { - clear: both; - } - .row-fluid [class*="span"] { - display: block; - float: left; - width: 100%; - min-height: 30px; - margin-left: 2.7624309392265194%; - *margin-left: 2.709239449864817%; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - } - .row-fluid [class*="span"]:first-child { - margin-left: 0; - } - .row-fluid .controls-row [class*="span"] + [class*="span"] { - margin-left: 2.7624309392265194%; - } - .row-fluid .span12 { - width: 100%; - *width: 99.94680851063829%; - } - .row-fluid .span11 { - width: 91.43646408839778%; - *width: 91.38327259903608%; - } - .row-fluid .span10 { - width: 82.87292817679558%; - *width: 82.81973668743387%; - } - .row-fluid .span9 { - width: 74.30939226519337%; - *width: 74.25620077583166%; - } - .row-fluid .span8 { - width: 65.74585635359117%; - *width: 65.69266486422946%; - } - .row-fluid .span7 { - width: 57.18232044198895%; - *width: 57.12912895262725%; - } - .row-fluid .span6 { - width: 48.61878453038674%; - *width: 48.56559304102504%; - } - .row-fluid .span5 { - width: 40.05524861878453%; - *width: 40.00205712942283%; - } - .row-fluid .span4 { - width: 31.491712707182323%; - *width: 31.43852121782062%; - } - .row-fluid .span3 { - width: 22.92817679558011%; - *width: 22.87498530621841%; - } - .row-fluid .span2 { - width: 14.3646408839779%; - *width: 14.311449394616199%; - } - .row-fluid .span1 { - width: 5.801104972375691%; - *width: 5.747913483013988%; - } - .row-fluid .offset12 { - margin-left: 105.52486187845304%; - *margin-left: 105.41847889972962%; - } - .row-fluid .offset12:first-child { - margin-left: 102.76243093922652%; - *margin-left: 102.6560479605031%; - } - .row-fluid .offset11 { - margin-left: 96.96132596685082%; - *margin-left: 96.8549429881274%; - } - .row-fluid .offset11:first-child { - margin-left: 94.1988950276243%; - *margin-left: 94.09251204890089%; - } - .row-fluid .offset10 { - margin-left: 88.39779005524862%; - *margin-left: 88.2914070765252%; - } - .row-fluid .offset10:first-child { - margin-left: 85.6353591160221%; - *margin-left: 85.52897613729868%; - } - .row-fluid .offset9 { - margin-left: 79.8342541436464%; - *margin-left: 79.72787116492299%; - } - .row-fluid .offset9:first-child { - margin-left: 77.07182320441989%; - *margin-left: 76.96544022569647%; - } - .row-fluid .offset8 { - margin-left: 71.2707182320442%; - *margin-left: 71.16433525332079%; - } - .row-fluid .offset8:first-child { - margin-left: 68.50828729281768%; - *margin-left: 68.40190431409427%; - } - .row-fluid .offset7 { - margin-left: 62.70718232044199%; - *margin-left: 62.600799341718584%; - } - .row-fluid .offset7:first-child { - margin-left: 59.94475138121547%; - *margin-left: 59.838368402492065%; - } - .row-fluid .offset6 { - margin-left: 54.14364640883978%; - *margin-left: 54.037263430116376%; - } - .row-fluid .offset6:first-child { - margin-left: 51.38121546961326%; - *margin-left: 51.27483249088986%; - } - .row-fluid .offset5 { - margin-left: 45.58011049723757%; - *margin-left: 45.47372751851417%; - } - .row-fluid .offset5:first-child { - margin-left: 42.81767955801105%; - *margin-left: 42.71129657928765%; - } - .row-fluid .offset4 { - margin-left: 37.01657458563536%; - *margin-left: 36.91019160691196%; - } - .row-fluid .offset4:first-child { - margin-left: 34.25414364640884%; - *margin-left: 34.14776066768544%; - } - .row-fluid .offset3 { - margin-left: 28.45303867403315%; - *margin-left: 28.346655695309746%; - } - .row-fluid .offset3:first-child { - margin-left: 25.69060773480663%; - *margin-left: 25.584224756083227%; - } - .row-fluid .offset2 { - margin-left: 19.88950276243094%; - *margin-left: 19.783119783707537%; - } - .row-fluid .offset2:first-child { - margin-left: 17.12707182320442%; - *margin-left: 17.02068884448102%; - } - .row-fluid .offset1 { - margin-left: 11.32596685082873%; - *margin-left: 11.219583872105325%; - } - .row-fluid .offset1:first-child { - margin-left: 8.56353591160221%; - *margin-left: 8.457152932878806%; - } - input, - textarea, - .uneditable-input { - margin-left: 0; - } - .controls-row [class*="span"] + [class*="span"] { - margin-left: 20px; - } - input.span12, - textarea.span12, - .uneditable-input.span12 { - width: 710px; - } - input.span11, - textarea.span11, - .uneditable-input.span11 { - width: 648px; - } - input.span10, - textarea.span10, - .uneditable-input.span10 { - width: 586px; - } - input.span9, - textarea.span9, - .uneditable-input.span9 { - width: 524px; - } - input.span8, - textarea.span8, - .uneditable-input.span8 { - width: 462px; - } - input.span7, - textarea.span7, - .uneditable-input.span7 { - width: 400px; - } - input.span6, - textarea.span6, - .uneditable-input.span6 { - width: 338px; - } - input.span5, - textarea.span5, - .uneditable-input.span5 { - width: 276px; - } - input.span4, - textarea.span4, - .uneditable-input.span4 { - width: 214px; - } - input.span3, - textarea.span3, - .uneditable-input.span3 { - width: 152px; - } - input.span2, - textarea.span2, - .uneditable-input.span2 { - width: 90px; - } - input.span1, - textarea.span1, - .uneditable-input.span1 { - width: 28px; - } -} - -@media (max-width: 767px) { - body { - padding-right: 20px; - padding-left: 20px; - } - .navbar-fixed-top, - .navbar-fixed-bottom, - .navbar-static-top { - margin-right: -20px; - margin-left: -20px; - } - .container-fluid { - padding: 0; - } - .dl-horizontal dt { - float: none; - width: auto; - clear: none; - text-align: left; - } - .dl-horizontal dd { - margin-left: 0; - } - .container { - width: auto; - } - .row-fluid { - width: 100%; - } - .row, - .thumbnails { - margin-left: 0; - } - .thumbnails > li { - float: none; - margin-left: 0; - } - [class*="span"], - .uneditable-input[class*="span"], - .row-fluid [class*="span"] { - display: block; - float: none; - width: 100%; - margin-left: 0; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - } - .span12, - .row-fluid .span12 { - width: 100%; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - } - .row-fluid [class*="offset"]:first-child { - margin-left: 0; - } - .input-large, - .input-xlarge, - .input-xxlarge, - input[class*="span"], - select[class*="span"], - textarea[class*="span"], - .uneditable-input { - display: block; - width: 100%; - min-height: 30px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - } - .input-prepend input, - .input-append input, - .input-prepend input[class*="span"], - .input-append input[class*="span"] { - display: inline-block; - width: auto; - } - .controls-row [class*="span"] + [class*="span"] { - margin-left: 0; - } - .modal { - position: fixed; - top: 20px; - right: 20px; - left: 20px; - width: auto; - margin: 0; - } - .modal.fade { - top: -100px; - } - .modal.fade.in { - top: 20px; - } -} - -@media (max-width: 480px) { - .nav-collapse { - -webkit-transform: translate3d(0, 0, 0); - } - .page-header h1 small { - display: block; - line-height: 20px; - } - input[type="checkbox"], - input[type="radio"] { - border: 1px solid #ccc; - } - .form-horizontal .control-label { - float: none; - width: auto; - padding-top: 0; - text-align: left; - } - .form-horizontal .controls { - margin-left: 0; - } - .form-horizontal .control-list { - padding-top: 0; - } - .form-horizontal .form-actions { - padding-right: 10px; - padding-left: 10px; - } - .media .pull-left, - .media .pull-right { - display: block; - float: none; - margin-bottom: 10px; - } - .media-object { - margin-right: 0; - margin-left: 0; - } - .modal { - top: 10px; - right: 10px; - left: 10px; - } - .modal-header .close { - padding: 10px; - margin: -10px; - } - .carousel-caption { - position: static; - } -} - -@media (max-width: 979px) { - body { - padding-top: 0; - } - .navbar-fixed-top, - .navbar-fixed-bottom { - position: static; - } - .navbar-fixed-top { - margin-bottom: 20px; - } - .navbar-fixed-bottom { - margin-top: 20px; - } - .navbar-fixed-top .navbar-inner, - .navbar-fixed-bottom .navbar-inner { - padding: 5px; - } - .navbar .container { - width: auto; - padding: 0; - } - .navbar .brand { - padding-right: 10px; - padding-left: 10px; - margin: 0 0 0 -5px; - } - .nav-collapse { - clear: both; - } - .nav-collapse .nav { - float: none; - margin: 0 0 10px; - } - .nav-collapse .nav > li { - float: none; - } - .nav-collapse .nav > li > a { - margin-bottom: 2px; - } - .nav-collapse .nav > .divider-vertical { - display: none; - } - .nav-collapse .nav .nav-header { - color: #777777; - text-shadow: none; - } - .nav-collapse .nav > li > a, - .nav-collapse .dropdown-menu a { - padding: 9px 15px; - font-weight: bold; - color: #777777; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - } - .nav-collapse .btn { - padding: 4px 10px 4px; - font-weight: normal; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - } - .nav-collapse .dropdown-menu li + li a { - margin-bottom: 2px; - } - .nav-collapse .nav > li > a:hover, - .nav-collapse .nav > li > a:focus, - .nav-collapse .dropdown-menu a:hover, - .nav-collapse .dropdown-menu a:focus { - background-color: #f2f2f2; - } - .navbar-inverse .nav-collapse .nav > li > a, - .navbar-inverse .nav-collapse .dropdown-menu a { - color: #999999; - } - .navbar-inverse .nav-collapse .nav > li > a:hover, - .navbar-inverse .nav-collapse .nav > li > a:focus, - .navbar-inverse .nav-collapse .dropdown-menu a:hover, - .navbar-inverse .nav-collapse .dropdown-menu a:focus { - background-color: #111111; - } - .nav-collapse.in .btn-group { - padding: 0; - margin-top: 5px; - } - .nav-collapse .dropdown-menu { - position: static; - top: auto; - left: auto; - display: none; - float: none; - max-width: none; - padding: 0; - margin: 0 15px; - background-color: transparent; - border: none; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - } - .nav-collapse .open > .dropdown-menu { - display: block; - } - .nav-collapse .dropdown-menu:before, - .nav-collapse .dropdown-menu:after { - display: none; - } - .nav-collapse .dropdown-menu .divider { - display: none; - } - .nav-collapse .nav > li > .dropdown-menu:before, - .nav-collapse .nav > li > .dropdown-menu:after { - display: none; - } - .nav-collapse .navbar-form, - .nav-collapse .navbar-search { - float: none; - padding: 10px 15px; - margin: 10px 0; - border-top: 1px solid #f2f2f2; - border-bottom: 1px solid #f2f2f2; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); - -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); - } - .navbar-inverse .nav-collapse .navbar-form, - .navbar-inverse .nav-collapse .navbar-search { - border-top-color: #111111; - border-bottom-color: #111111; - } - .navbar .nav-collapse .nav.pull-right { - float: none; - margin-left: 0; - } - .nav-collapse, - .nav-collapse.collapse { - height: 0; - overflow: hidden; - } - .navbar .btn-navbar { - display: block; - } - .navbar-static .navbar-inner { - padding-right: 10px; - padding-left: 10px; - } -} - -@media (min-width: 980px) { - .nav-collapse.collapse { - height: auto !important; - overflow: visible !important; - } -} diff --git a/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.min.css b/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.min.css deleted file mode 100644 index 96a435be90..0000000000 --- a/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.min.css +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * Bootstrap Responsive v2.3.2 - * - * Copyright 2013 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world by @mdo and @fat. - */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} diff --git a/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.css b/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.css deleted file mode 100644 index 5b7fe7e856..0000000000 --- a/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.css +++ /dev/null @@ -1,6167 +0,0 @@ -/*! - * Bootstrap v2.3.2 - * - * Copyright 2013 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world by @mdo and @fat. - */ - -.clearfix { - *zoom: 1; -} - -.clearfix:before, -.clearfix:after { - display: table; - line-height: 0; - content: ""; -} - -.clearfix:after { - clear: both; -} - -.hide-text { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} - -.input-block-level { - display: block; - width: 100%; - min-height: 30px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -nav, -section { - display: block; -} - -audio, -canvas, -video { - display: inline-block; - *display: inline; - *zoom: 1; -} - -audio:not([controls]) { - display: none; -} - -html { - font-size: 100%; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} - -a:focus { - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} - -a:hover, -a:active { - outline: 0; -} - -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} - -sup { - top: -0.5em; -} - -sub { - bottom: -0.25em; -} - -img { - width: auto\9; - height: auto; - max-width: 100%; - vertical-align: middle; - border: 0; - -ms-interpolation-mode: bicubic; -} - -#map_canvas img, -.google-maps img { - max-width: none; -} - -button, -input, -select, -textarea { - margin: 0; - font-size: 100%; - vertical-align: middle; -} - -button, -input { - *overflow: visible; - line-height: normal; -} - -button::-moz-focus-inner, -input::-moz-focus-inner { - padding: 0; - border: 0; -} - -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] { - cursor: pointer; - -webkit-appearance: button; -} - -label, -select, -button, -input[type="button"], -input[type="reset"], -input[type="submit"], -input[type="radio"], -input[type="checkbox"] { - cursor: pointer; -} - -input[type="search"] { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; - -webkit-appearance: textfield; -} - -input[type="search"]::-webkit-search-decoration, -input[type="search"]::-webkit-search-cancel-button { - -webkit-appearance: none; -} - -textarea { - overflow: auto; - vertical-align: top; -} - -@media print { - * { - color: #000 !important; - text-shadow: none !important; - background: transparent !important; - box-shadow: none !important; - } - a, - a:visited { - text-decoration: underline; - } - a[href]:after { - content: " (" attr(href) ")"; - } - abbr[title]:after { - content: " (" attr(title) ")"; - } - .ir a:after, - a[href^="javascript:"]:after, - a[href^="#"]:after { - content: ""; - } - pre, - blockquote { - border: 1px solid #999; - page-break-inside: avoid; - } - thead { - display: table-header-group; - } - tr, - img { - page-break-inside: avoid; - } - img { - max-width: 100% !important; - } - @page { - margin: 0.5cm; - } - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - h2, - h3 { - page-break-after: avoid; - } -} - -body { - margin: 0; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 20px; - color: #333333; - background-color: #ffffff; -} - -a { - color: #0088cc; - text-decoration: none; -} - -a:hover, -a:focus { - color: #005580; - text-decoration: underline; -} - -.img-rounded { - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} - -.img-polaroid { - padding: 4px; - background-color: #fff; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); -} - -.img-circle { - -webkit-border-radius: 500px; - -moz-border-radius: 500px; - border-radius: 500px; -} - -.row { - margin-left: -20px; - *zoom: 1; -} - -.row:before, -.row:after { - display: table; - line-height: 0; - content: ""; -} - -.row:after { - clear: both; -} - -[class*="span"] { - float: left; - min-height: 1px; - margin-left: 20px; -} - -.container, -.navbar-static-top .container, -.navbar-fixed-top .container, -.navbar-fixed-bottom .container { - width: 940px; -} - -.span12 { - width: 940px; -} - -.span11 { - width: 860px; -} - -.span10 { - width: 780px; -} - -.span9 { - width: 700px; -} - -.span8 { - width: 620px; -} - -.span7 { - width: 540px; -} - -.span6 { - width: 460px; -} - -.span5 { - width: 380px; -} - -.span4 { - width: 300px; -} - -.span3 { - width: 220px; -} - -.span2 { - width: 140px; -} - -.span1 { - width: 60px; -} - -.offset12 { - margin-left: 980px; -} - -.offset11 { - margin-left: 900px; -} - -.offset10 { - margin-left: 820px; -} - -.offset9 { - margin-left: 740px; -} - -.offset8 { - margin-left: 660px; -} - -.offset7 { - margin-left: 580px; -} - -.offset6 { - margin-left: 500px; -} - -.offset5 { - margin-left: 420px; -} - -.offset4 { - margin-left: 340px; -} - -.offset3 { - margin-left: 260px; -} - -.offset2 { - margin-left: 180px; -} - -.offset1 { - margin-left: 100px; -} - -.row-fluid { - width: 100%; - *zoom: 1; -} - -.row-fluid:before, -.row-fluid:after { - display: table; - line-height: 0; - content: ""; -} - -.row-fluid:after { - clear: both; -} - -.row-fluid [class*="span"] { - display: block; - float: left; - width: 100%; - min-height: 30px; - margin-left: 2.127659574468085%; - *margin-left: 2.074468085106383%; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -.row-fluid [class*="span"]:first-child { - margin-left: 0; -} - -.row-fluid .controls-row [class*="span"] + [class*="span"] { - margin-left: 2.127659574468085%; -} - -.row-fluid .span12 { - width: 100%; - *width: 99.94680851063829%; -} - -.row-fluid .span11 { - width: 91.48936170212765%; - *width: 91.43617021276594%; -} - -.row-fluid .span10 { - width: 82.97872340425532%; - *width: 82.92553191489361%; -} - -.row-fluid .span9 { - width: 74.46808510638297%; - *width: 74.41489361702126%; -} - -.row-fluid .span8 { - width: 65.95744680851064%; - *width: 65.90425531914893%; -} - -.row-fluid .span7 { - width: 57.44680851063829%; - *width: 57.39361702127659%; -} - -.row-fluid .span6 { - width: 48.93617021276595%; - *width: 48.88297872340425%; -} - -.row-fluid .span5 { - width: 40.42553191489362%; - *width: 40.37234042553192%; -} - -.row-fluid .span4 { - width: 31.914893617021278%; - *width: 31.861702127659576%; -} - -.row-fluid .span3 { - width: 23.404255319148934%; - *width: 23.351063829787233%; -} - -.row-fluid .span2 { - width: 14.893617021276595%; - *width: 14.840425531914894%; -} - -.row-fluid .span1 { - width: 6.382978723404255%; - *width: 6.329787234042553%; -} - -.row-fluid .offset12 { - margin-left: 104.25531914893617%; - *margin-left: 104.14893617021275%; -} - -.row-fluid .offset12:first-child { - margin-left: 102.12765957446808%; - *margin-left: 102.02127659574467%; -} - -.row-fluid .offset11 { - margin-left: 95.74468085106382%; - *margin-left: 95.6382978723404%; -} - -.row-fluid .offset11:first-child { - margin-left: 93.61702127659574%; - *margin-left: 93.51063829787232%; -} - -.row-fluid .offset10 { - margin-left: 87.23404255319149%; - *margin-left: 87.12765957446807%; -} - -.row-fluid .offset10:first-child { - margin-left: 85.1063829787234%; - *margin-left: 84.99999999999999%; -} - -.row-fluid .offset9 { - margin-left: 78.72340425531914%; - *margin-left: 78.61702127659572%; -} - -.row-fluid .offset9:first-child { - margin-left: 76.59574468085106%; - *margin-left: 76.48936170212764%; -} - -.row-fluid .offset8 { - margin-left: 70.2127659574468%; - *margin-left: 70.10638297872339%; -} - -.row-fluid .offset8:first-child { - margin-left: 68.08510638297872%; - *margin-left: 67.9787234042553%; -} - -.row-fluid .offset7 { - margin-left: 61.70212765957446%; - *margin-left: 61.59574468085106%; -} - -.row-fluid .offset7:first-child { - margin-left: 59.574468085106375%; - *margin-left: 59.46808510638297%; -} - -.row-fluid .offset6 { - margin-left: 53.191489361702125%; - *margin-left: 53.085106382978715%; -} - -.row-fluid .offset6:first-child { - margin-left: 51.063829787234035%; - *margin-left: 50.95744680851063%; -} - -.row-fluid .offset5 { - margin-left: 44.68085106382979%; - *margin-left: 44.57446808510638%; -} - -.row-fluid .offset5:first-child { - margin-left: 42.5531914893617%; - *margin-left: 42.4468085106383%; -} - -.row-fluid .offset4 { - margin-left: 36.170212765957444%; - *margin-left: 36.06382978723405%; -} - -.row-fluid .offset4:first-child { - margin-left: 34.04255319148936%; - *margin-left: 33.93617021276596%; -} - -.row-fluid .offset3 { - margin-left: 27.659574468085104%; - *margin-left: 27.5531914893617%; -} - -.row-fluid .offset3:first-child { - margin-left: 25.53191489361702%; - *margin-left: 25.425531914893618%; -} - -.row-fluid .offset2 { - margin-left: 19.148936170212764%; - *margin-left: 19.04255319148936%; -} - -.row-fluid .offset2:first-child { - margin-left: 17.02127659574468%; - *margin-left: 16.914893617021278%; -} - -.row-fluid .offset1 { - margin-left: 10.638297872340425%; - *margin-left: 10.53191489361702%; -} - -.row-fluid .offset1:first-child { - margin-left: 8.51063829787234%; - *margin-left: 8.404255319148938%; -} - -[class*="span"].hide, -.row-fluid [class*="span"].hide { - display: none; -} - -[class*="span"].pull-right, -.row-fluid [class*="span"].pull-right { - float: right; -} - -.container { - margin-right: auto; - margin-left: auto; - *zoom: 1; -} - -.container:before, -.container:after { - display: table; - line-height: 0; - content: ""; -} - -.container:after { - clear: both; -} - -.container-fluid { - padding-right: 20px; - padding-left: 20px; - *zoom: 1; -} - -.container-fluid:before, -.container-fluid:after { - display: table; - line-height: 0; - content: ""; -} - -.container-fluid:after { - clear: both; -} - -p { - margin: 0 0 10px; -} - -.lead { - margin-bottom: 20px; - font-size: 21px; - font-weight: 200; - line-height: 30px; -} - -small { - font-size: 85%; -} - -strong { - font-weight: bold; -} - -em { - font-style: italic; -} - -cite { - font-style: normal; -} - -.muted { - color: #999999; -} - -a.muted:hover, -a.muted:focus { - color: #808080; -} - -.text-warning { - color: #c09853; -} - -a.text-warning:hover, -a.text-warning:focus { - color: #a47e3c; -} - -.text-error { - color: #b94a48; -} - -a.text-error:hover, -a.text-error:focus { - color: #953b39; -} - -.text-info { - color: #3a87ad; -} - -a.text-info:hover, -a.text-info:focus { - color: #2d6987; -} - -.text-success { - color: #468847; -} - -a.text-success:hover, -a.text-success:focus { - color: #356635; -} - -.text-left { - text-align: left; -} - -.text-right { - text-align: right; -} - -.text-center { - text-align: center; -} - -h1, -h2, -h3, -h4, -h5, -h6 { - margin: 10px 0; - font-family: inherit; - font-weight: bold; - line-height: 20px; - color: inherit; - text-rendering: optimizelegibility; -} - -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small { - font-weight: normal; - line-height: 1; - color: #999999; -} - -h1, -h2, -h3 { - line-height: 40px; -} - -h1 { - font-size: 38.5px; -} - -h2 { - font-size: 31.5px; -} - -h3 { - font-size: 24.5px; -} - -h4 { - font-size: 17.5px; -} - -h5 { - font-size: 14px; -} - -h6 { - font-size: 11.9px; -} - -h1 small { - font-size: 24.5px; -} - -h2 small { - font-size: 17.5px; -} - -h3 small { - font-size: 14px; -} - -h4 small { - font-size: 14px; -} - -.page-header { - padding-bottom: 9px; - margin: 20px 0 30px; - border-bottom: 1px solid #eeeeee; -} - -ul, -ol { - padding: 0; - margin: 0 0 10px 25px; -} - -ul ul, -ul ol, -ol ol, -ol ul { - margin-bottom: 0; -} - -li { - line-height: 20px; -} - -ul.unstyled, -ol.unstyled { - margin-left: 0; - list-style: none; -} - -ul.inline, -ol.inline { - margin-left: 0; - list-style: none; -} - -ul.inline > li, -ol.inline > li { - display: inline-block; - *display: inline; - padding-right: 5px; - padding-left: 5px; - *zoom: 1; -} - -dl { - margin-bottom: 20px; -} - -dt, -dd { - line-height: 20px; -} - -dt { - font-weight: bold; -} - -dd { - margin-left: 10px; -} - -.dl-horizontal { - *zoom: 1; -} - -.dl-horizontal:before, -.dl-horizontal:after { - display: table; - line-height: 0; - content: ""; -} - -.dl-horizontal:after { - clear: both; -} - -.dl-horizontal dt { - float: left; - width: 160px; - overflow: hidden; - clear: left; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; -} - -.dl-horizontal dd { - margin-left: 180px; -} - -hr { - margin: 20px 0; - border: 0; - border-top: 1px solid #eeeeee; - border-bottom: 1px solid #ffffff; -} - -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #999999; -} - -abbr.initialism { - font-size: 90%; - text-transform: uppercase; -} - -blockquote { - padding: 0 0 0 15px; - margin: 0 0 20px; - border-left: 5px solid #eeeeee; -} - -blockquote p { - margin-bottom: 0; - font-size: 17.5px; - font-weight: 300; - line-height: 1.25; -} - -blockquote small { - display: block; - line-height: 20px; - color: #999999; -} - -blockquote small:before { - content: '\2014 \00A0'; -} - -blockquote.pull-right { - float: right; - padding-right: 15px; - padding-left: 0; - border-right: 5px solid #eeeeee; - border-left: 0; -} - -blockquote.pull-right p, -blockquote.pull-right small { - text-align: right; -} - -blockquote.pull-right small:before { - content: ''; -} - -blockquote.pull-right small:after { - content: '\00A0 \2014'; -} - -q:before, -q:after, -blockquote:before, -blockquote:after { - content: ""; -} - -address { - display: block; - margin-bottom: 20px; - font-style: normal; - line-height: 20px; -} - -code, -pre { - padding: 0 3px 2px; - font-family: Monaco, Menlo, Consolas, "Courier New", monospace; - font-size: 12px; - color: #333333; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -code { - padding: 2px 4px; - color: #d14; - white-space: nowrap; - background-color: #f7f7f9; - border: 1px solid #e1e1e8; -} - -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 20px; - word-break: break-all; - word-wrap: break-word; - white-space: pre; - white-space: pre-wrap; - background-color: #f5f5f5; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.15); - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -pre.prettyprint { - margin-bottom: 20px; -} - -pre code { - padding: 0; - color: inherit; - white-space: pre; - white-space: pre-wrap; - background-color: transparent; - border: 0; -} - -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} - -form { - margin: 0 0 20px; -} - -fieldset { - padding: 0; - margin: 0; - border: 0; -} - -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: 40px; - color: #333333; - border: 0; - border-bottom: 1px solid #e5e5e5; -} - -legend small { - font-size: 15px; - color: #999999; -} - -label, -input, -button, -select, -textarea { - font-size: 14px; - font-weight: normal; - line-height: 20px; -} - -input, -button, -select, -textarea { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; -} - -label { - display: block; - margin-bottom: 5px; -} - -select, -textarea, -input[type="text"], -input[type="password"], -input[type="datetime"], -input[type="datetime-local"], -input[type="date"], -input[type="month"], -input[type="time"], -input[type="week"], -input[type="number"], -input[type="email"], -input[type="url"], -input[type="search"], -input[type="tel"], -input[type="color"], -.uneditable-input { - display: inline-block; - height: 20px; - padding: 4px 6px; - margin-bottom: 10px; - font-size: 14px; - line-height: 20px; - color: #555555; - vertical-align: middle; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -input, -textarea, -.uneditable-input { - width: 206px; -} - -textarea { - height: auto; -} - -textarea, -input[type="text"], -input[type="password"], -input[type="datetime"], -input[type="datetime-local"], -input[type="date"], -input[type="month"], -input[type="time"], -input[type="week"], -input[type="number"], -input[type="email"], -input[type="url"], -input[type="search"], -input[type="tel"], -input[type="color"], -.uneditable-input { - background-color: #ffffff; - border: 1px solid #cccccc; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; - -moz-transition: border linear 0.2s, box-shadow linear 0.2s; - -o-transition: border linear 0.2s, box-shadow linear 0.2s; - transition: border linear 0.2s, box-shadow linear 0.2s; -} - -textarea:focus, -input[type="text"]:focus, -input[type="password"]:focus, -input[type="datetime"]:focus, -input[type="datetime-local"]:focus, -input[type="date"]:focus, -input[type="month"]:focus, -input[type="time"]:focus, -input[type="week"]:focus, -input[type="number"]:focus, -input[type="email"]:focus, -input[type="url"]:focus, -input[type="search"]:focus, -input[type="tel"]:focus, -input[type="color"]:focus, -.uneditable-input:focus { - border-color: rgba(82, 168, 236, 0.8); - outline: 0; - outline: thin dotted \9; - /* IE6-9 */ - - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); -} - -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; - *margin-top: 0; - line-height: normal; -} - -input[type="file"], -input[type="image"], -input[type="submit"], -input[type="reset"], -input[type="button"], -input[type="radio"], -input[type="checkbox"] { - width: auto; -} - -select, -input[type="file"] { - height: 30px; - /* In IE7, the height of the select element cannot be changed by height, only font-size */ - - *margin-top: 4px; - /* For IE7, add top margin to align select with labels */ - - line-height: 30px; -} - -select { - width: 220px; - background-color: #ffffff; - border: 1px solid #cccccc; -} - -select[multiple], -select[size] { - height: auto; -} - -select:focus, -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} - -.uneditable-input, -.uneditable-textarea { - color: #999999; - cursor: not-allowed; - background-color: #fcfcfc; - border-color: #cccccc; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); -} - -.uneditable-input { - overflow: hidden; - white-space: nowrap; -} - -.uneditable-textarea { - width: auto; - height: auto; -} - -input:-moz-placeholder, -textarea:-moz-placeholder { - color: #999999; -} - -input:-ms-input-placeholder, -textarea:-ms-input-placeholder { - color: #999999; -} - -input::-webkit-input-placeholder, -textarea::-webkit-input-placeholder { - color: #999999; -} - -.radio, -.checkbox { - min-height: 20px; - padding-left: 20px; -} - -.radio input[type="radio"], -.checkbox input[type="checkbox"] { - float: left; - margin-left: -20px; -} - -.controls > .radio:first-child, -.controls > .checkbox:first-child { - padding-top: 5px; -} - -.radio.inline, -.checkbox.inline { - display: inline-block; - padding-top: 5px; - margin-bottom: 0; - vertical-align: middle; -} - -.radio.inline + .radio.inline, -.checkbox.inline + .checkbox.inline { - margin-left: 10px; -} - -.input-mini { - width: 60px; -} - -.input-small { - width: 90px; -} - -.input-medium { - width: 150px; -} - -.input-large { - width: 210px; -} - -.input-xlarge { - width: 270px; -} - -.input-xxlarge { - width: 530px; -} - -input[class*="span"], -select[class*="span"], -textarea[class*="span"], -.uneditable-input[class*="span"], -.row-fluid input[class*="span"], -.row-fluid select[class*="span"], -.row-fluid textarea[class*="span"], -.row-fluid .uneditable-input[class*="span"] { - float: none; - margin-left: 0; -} - -.input-append input[class*="span"], -.input-append .uneditable-input[class*="span"], -.input-prepend input[class*="span"], -.input-prepend .uneditable-input[class*="span"], -.row-fluid input[class*="span"], -.row-fluid select[class*="span"], -.row-fluid textarea[class*="span"], -.row-fluid .uneditable-input[class*="span"], -.row-fluid .input-prepend [class*="span"], -.row-fluid .input-append [class*="span"] { - display: inline-block; -} - -input, -textarea, -.uneditable-input { - margin-left: 0; -} - -.controls-row [class*="span"] + [class*="span"] { - margin-left: 20px; -} - -input.span12, -textarea.span12, -.uneditable-input.span12 { - width: 926px; -} - -input.span11, -textarea.span11, -.uneditable-input.span11 { - width: 846px; -} - -input.span10, -textarea.span10, -.uneditable-input.span10 { - width: 766px; -} - -input.span9, -textarea.span9, -.uneditable-input.span9 { - width: 686px; -} - -input.span8, -textarea.span8, -.uneditable-input.span8 { - width: 606px; -} - -input.span7, -textarea.span7, -.uneditable-input.span7 { - width: 526px; -} - -input.span6, -textarea.span6, -.uneditable-input.span6 { - width: 446px; -} - -input.span5, -textarea.span5, -.uneditable-input.span5 { - width: 366px; -} - -input.span4, -textarea.span4, -.uneditable-input.span4 { - width: 286px; -} - -input.span3, -textarea.span3, -.uneditable-input.span3 { - width: 206px; -} - -input.span2, -textarea.span2, -.uneditable-input.span2 { - width: 126px; -} - -input.span1, -textarea.span1, -.uneditable-input.span1 { - width: 46px; -} - -.controls-row { - *zoom: 1; -} - -.controls-row:before, -.controls-row:after { - display: table; - line-height: 0; - content: ""; -} - -.controls-row:after { - clear: both; -} - -.controls-row [class*="span"], -.row-fluid .controls-row [class*="span"] { - float: left; -} - -.controls-row .checkbox[class*="span"], -.controls-row .radio[class*="span"] { - padding-top: 5px; -} - -input[disabled], -select[disabled], -textarea[disabled], -input[readonly], -select[readonly], -textarea[readonly] { - cursor: not-allowed; - background-color: #eeeeee; -} - -input[type="radio"][disabled], -input[type="checkbox"][disabled], -input[type="radio"][readonly], -input[type="checkbox"][readonly] { - background-color: transparent; -} - -.control-group.warning .control-label, -.control-group.warning .help-block, -.control-group.warning .help-inline { - color: #c09853; -} - -.control-group.warning .checkbox, -.control-group.warning .radio, -.control-group.warning input, -.control-group.warning select, -.control-group.warning textarea { - color: #c09853; -} - -.control-group.warning input, -.control-group.warning select, -.control-group.warning textarea { - border-color: #c09853; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} - -.control-group.warning input:focus, -.control-group.warning select:focus, -.control-group.warning textarea:focus { - border-color: #a47e3c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; -} - -.control-group.warning .input-prepend .add-on, -.control-group.warning .input-append .add-on { - color: #c09853; - background-color: #fcf8e3; - border-color: #c09853; -} - -.control-group.error .control-label, -.control-group.error .help-block, -.control-group.error .help-inline { - color: #b94a48; -} - -.control-group.error .checkbox, -.control-group.error .radio, -.control-group.error input, -.control-group.error select, -.control-group.error textarea { - color: #b94a48; -} - -.control-group.error input, -.control-group.error select, -.control-group.error textarea { - border-color: #b94a48; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} - -.control-group.error input:focus, -.control-group.error select:focus, -.control-group.error textarea:focus { - border-color: #953b39; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; -} - -.control-group.error .input-prepend .add-on, -.control-group.error .input-append .add-on { - color: #b94a48; - background-color: #f2dede; - border-color: #b94a48; -} - -.control-group.success .control-label, -.control-group.success .help-block, -.control-group.success .help-inline { - color: #468847; -} - -.control-group.success .checkbox, -.control-group.success .radio, -.control-group.success input, -.control-group.success select, -.control-group.success textarea { - color: #468847; -} - -.control-group.success input, -.control-group.success select, -.control-group.success textarea { - border-color: #468847; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} - -.control-group.success input:focus, -.control-group.success select:focus, -.control-group.success textarea:focus { - border-color: #356635; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; -} - -.control-group.success .input-prepend .add-on, -.control-group.success .input-append .add-on { - color: #468847; - background-color: #dff0d8; - border-color: #468847; -} - -.control-group.info .control-label, -.control-group.info .help-block, -.control-group.info .help-inline { - color: #3a87ad; -} - -.control-group.info .checkbox, -.control-group.info .radio, -.control-group.info input, -.control-group.info select, -.control-group.info textarea { - color: #3a87ad; -} - -.control-group.info input, -.control-group.info select, -.control-group.info textarea { - border-color: #3a87ad; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} - -.control-group.info input:focus, -.control-group.info select:focus, -.control-group.info textarea:focus { - border-color: #2d6987; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; -} - -.control-group.info .input-prepend .add-on, -.control-group.info .input-append .add-on { - color: #3a87ad; - background-color: #d9edf7; - border-color: #3a87ad; -} - -input:focus:invalid, -textarea:focus:invalid, -select:focus:invalid { - color: #b94a48; - border-color: #ee5f5b; -} - -input:focus:invalid:focus, -textarea:focus:invalid:focus, -select:focus:invalid:focus { - border-color: #e9322d; - -webkit-box-shadow: 0 0 6px #f8b9b7; - -moz-box-shadow: 0 0 6px #f8b9b7; - box-shadow: 0 0 6px #f8b9b7; -} - -.form-actions { - padding: 19px 20px 20px; - margin-top: 20px; - margin-bottom: 20px; - background-color: #f5f5f5; - border-top: 1px solid #e5e5e5; - *zoom: 1; -} - -.form-actions:before, -.form-actions:after { - display: table; - line-height: 0; - content: ""; -} - -.form-actions:after { - clear: both; -} - -.help-block, -.help-inline { - color: #595959; -} - -.help-block { - display: block; - margin-bottom: 10px; -} - -.help-inline { - display: inline-block; - *display: inline; - padding-left: 5px; - vertical-align: middle; - *zoom: 1; -} - -.input-append, -.input-prepend { - display: inline-block; - margin-bottom: 10px; - font-size: 0; - white-space: nowrap; - vertical-align: middle; -} - -.input-append input, -.input-prepend input, -.input-append select, -.input-prepend select, -.input-append .uneditable-input, -.input-prepend .uneditable-input, -.input-append .dropdown-menu, -.input-prepend .dropdown-menu, -.input-append .popover, -.input-prepend .popover { - font-size: 14px; -} - -.input-append input, -.input-prepend input, -.input-append select, -.input-prepend select, -.input-append .uneditable-input, -.input-prepend .uneditable-input { - position: relative; - margin-bottom: 0; - *margin-left: 0; - vertical-align: top; - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} - -.input-append input:focus, -.input-prepend input:focus, -.input-append select:focus, -.input-prepend select:focus, -.input-append .uneditable-input:focus, -.input-prepend .uneditable-input:focus { - z-index: 2; -} - -.input-append .add-on, -.input-prepend .add-on { - display: inline-block; - width: auto; - height: 20px; - min-width: 16px; - padding: 4px 5px; - font-size: 14px; - font-weight: normal; - line-height: 20px; - text-align: center; - text-shadow: 0 1px 0 #ffffff; - background-color: #eeeeee; - border: 1px solid #ccc; -} - -.input-append .add-on, -.input-prepend .add-on, -.input-append .btn, -.input-prepend .btn, -.input-append .btn-group > .dropdown-toggle, -.input-prepend .btn-group > .dropdown-toggle { - vertical-align: top; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.input-append .active, -.input-prepend .active { - background-color: #a9dba9; - border-color: #46a546; -} - -.input-prepend .add-on, -.input-prepend .btn { - margin-right: -1px; -} - -.input-prepend .add-on:first-child, -.input-prepend .btn:first-child { - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} - -.input-append input, -.input-append select, -.input-append .uneditable-input { - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} - -.input-append input + .btn-group .btn:last-child, -.input-append select + .btn-group .btn:last-child, -.input-append .uneditable-input + .btn-group .btn:last-child { - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} - -.input-append .add-on, -.input-append .btn, -.input-append .btn-group { - margin-left: -1px; -} - -.input-append .add-on:last-child, -.input-append .btn:last-child, -.input-append .btn-group:last-child > .dropdown-toggle { - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} - -.input-prepend.input-append input, -.input-prepend.input-append select, -.input-prepend.input-append .uneditable-input { - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.input-prepend.input-append input + .btn-group .btn, -.input-prepend.input-append select + .btn-group .btn, -.input-prepend.input-append .uneditable-input + .btn-group .btn { - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} - -.input-prepend.input-append .add-on:first-child, -.input-prepend.input-append .btn:first-child { - margin-right: -1px; - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} - -.input-prepend.input-append .add-on:last-child, -.input-prepend.input-append .btn:last-child { - margin-left: -1px; - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} - -.input-prepend.input-append .btn-group:first-child { - margin-left: 0; -} - -input.search-query { - padding-right: 14px; - padding-right: 4px \9; - padding-left: 14px; - padding-left: 4px \9; - /* IE7-8 doesn't have border-radius, so don't indent the padding */ - - margin-bottom: 0; - -webkit-border-radius: 15px; - -moz-border-radius: 15px; - border-radius: 15px; -} - -/* Allow for input prepend/append in search forms */ - -.form-search .input-append .search-query, -.form-search .input-prepend .search-query { - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.form-search .input-append .search-query { - -webkit-border-radius: 14px 0 0 14px; - -moz-border-radius: 14px 0 0 14px; - border-radius: 14px 0 0 14px; -} - -.form-search .input-append .btn { - -webkit-border-radius: 0 14px 14px 0; - -moz-border-radius: 0 14px 14px 0; - border-radius: 0 14px 14px 0; -} - -.form-search .input-prepend .search-query { - -webkit-border-radius: 0 14px 14px 0; - -moz-border-radius: 0 14px 14px 0; - border-radius: 0 14px 14px 0; -} - -.form-search .input-prepend .btn { - -webkit-border-radius: 14px 0 0 14px; - -moz-border-radius: 14px 0 0 14px; - border-radius: 14px 0 0 14px; -} - -.form-search input, -.form-inline input, -.form-horizontal input, -.form-search textarea, -.form-inline textarea, -.form-horizontal textarea, -.form-search select, -.form-inline select, -.form-horizontal select, -.form-search .help-inline, -.form-inline .help-inline, -.form-horizontal .help-inline, -.form-search .uneditable-input, -.form-inline .uneditable-input, -.form-horizontal .uneditable-input, -.form-search .input-prepend, -.form-inline .input-prepend, -.form-horizontal .input-prepend, -.form-search .input-append, -.form-inline .input-append, -.form-horizontal .input-append { - display: inline-block; - *display: inline; - margin-bottom: 0; - vertical-align: middle; - *zoom: 1; -} - -.form-search .hide, -.form-inline .hide, -.form-horizontal .hide { - display: none; -} - -.form-search label, -.form-inline label, -.form-search .btn-group, -.form-inline .btn-group { - display: inline-block; -} - -.form-search .input-append, -.form-inline .input-append, -.form-search .input-prepend, -.form-inline .input-prepend { - margin-bottom: 0; -} - -.form-search .radio, -.form-search .checkbox, -.form-inline .radio, -.form-inline .checkbox { - padding-left: 0; - margin-bottom: 0; - vertical-align: middle; -} - -.form-search .radio input[type="radio"], -.form-search .checkbox input[type="checkbox"], -.form-inline .radio input[type="radio"], -.form-inline .checkbox input[type="checkbox"] { - float: left; - margin-right: 3px; - margin-left: 0; -} - -.control-group { - margin-bottom: 10px; -} - -legend + .control-group { - margin-top: 20px; - -webkit-margin-top-collapse: separate; -} - -.form-horizontal .control-group { - margin-bottom: 20px; - *zoom: 1; -} - -.form-horizontal .control-group:before, -.form-horizontal .control-group:after { - display: table; - line-height: 0; - content: ""; -} - -.form-horizontal .control-group:after { - clear: both; -} - -.form-horizontal .control-label { - float: left; - width: 160px; - padding-top: 5px; - text-align: right; -} - -.form-horizontal .controls { - *display: inline-block; - *padding-left: 20px; - margin-left: 180px; - *margin-left: 0; -} - -.form-horizontal .controls:first-child { - *padding-left: 180px; -} - -.form-horizontal .help-block { - margin-bottom: 0; -} - -.form-horizontal input + .help-block, -.form-horizontal select + .help-block, -.form-horizontal textarea + .help-block, -.form-horizontal .uneditable-input + .help-block, -.form-horizontal .input-prepend + .help-block, -.form-horizontal .input-append + .help-block { - margin-top: 10px; -} - -.form-horizontal .form-actions { - padding-left: 180px; -} - -table { - max-width: 100%; - background-color: transparent; - border-collapse: collapse; - border-spacing: 0; -} - -.table { - width: 100%; - margin-bottom: 20px; -} - -.table th, -.table td { - padding: 8px; - line-height: 20px; - text-align: left; - vertical-align: top; - border-top: 1px solid #dddddd; -} - -.table th { - font-weight: bold; -} - -.table thead th { - vertical-align: bottom; -} - -.table caption + thead tr:first-child th, -.table caption + thead tr:first-child td, -.table colgroup + thead tr:first-child th, -.table colgroup + thead tr:first-child td, -.table thead:first-child tr:first-child th, -.table thead:first-child tr:first-child td { - border-top: 0; -} - -.table tbody + tbody { - border-top: 2px solid #dddddd; -} - -.table .table { - background-color: #ffffff; -} - -.table-condensed th, -.table-condensed td { - padding: 4px 5px; -} - -.table-bordered { - border: 1px solid #dddddd; - border-collapse: separate; - *border-collapse: collapse; - border-left: 0; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -.table-bordered th, -.table-bordered td { - border-left: 1px solid #dddddd; -} - -.table-bordered caption + thead tr:first-child th, -.table-bordered caption + tbody tr:first-child th, -.table-bordered caption + tbody tr:first-child td, -.table-bordered colgroup + thead tr:first-child th, -.table-bordered colgroup + tbody tr:first-child th, -.table-bordered colgroup + tbody tr:first-child td, -.table-bordered thead:first-child tr:first-child th, -.table-bordered tbody:first-child tr:first-child th, -.table-bordered tbody:first-child tr:first-child td { - border-top: 0; -} - -.table-bordered thead:first-child tr:first-child > th:first-child, -.table-bordered tbody:first-child tr:first-child > td:first-child, -.table-bordered tbody:first-child tr:first-child > th:first-child { - -webkit-border-top-left-radius: 4px; - border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; -} - -.table-bordered thead:first-child tr:first-child > th:last-child, -.table-bordered tbody:first-child tr:first-child > td:last-child, -.table-bordered tbody:first-child tr:first-child > th:last-child { - -webkit-border-top-right-radius: 4px; - border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; -} - -.table-bordered thead:last-child tr:last-child > th:first-child, -.table-bordered tbody:last-child tr:last-child > td:first-child, -.table-bordered tbody:last-child tr:last-child > th:first-child, -.table-bordered tfoot:last-child tr:last-child > td:first-child, -.table-bordered tfoot:last-child tr:last-child > th:first-child { - -webkit-border-bottom-left-radius: 4px; - border-bottom-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; -} - -.table-bordered thead:last-child tr:last-child > th:last-child, -.table-bordered tbody:last-child tr:last-child > td:last-child, -.table-bordered tbody:last-child tr:last-child > th:last-child, -.table-bordered tfoot:last-child tr:last-child > td:last-child, -.table-bordered tfoot:last-child tr:last-child > th:last-child { - -webkit-border-bottom-right-radius: 4px; - border-bottom-right-radius: 4px; - -moz-border-radius-bottomright: 4px; -} - -.table-bordered tfoot + tbody:last-child tr:last-child td:first-child { - -webkit-border-bottom-left-radius: 0; - border-bottom-left-radius: 0; - -moz-border-radius-bottomleft: 0; -} - -.table-bordered tfoot + tbody:last-child tr:last-child td:last-child { - -webkit-border-bottom-right-radius: 0; - border-bottom-right-radius: 0; - -moz-border-radius-bottomright: 0; -} - -.table-bordered caption + thead tr:first-child th:first-child, -.table-bordered caption + tbody tr:first-child td:first-child, -.table-bordered colgroup + thead tr:first-child th:first-child, -.table-bordered colgroup + tbody tr:first-child td:first-child { - -webkit-border-top-left-radius: 4px; - border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; -} - -.table-bordered caption + thead tr:first-child th:last-child, -.table-bordered caption + tbody tr:first-child td:last-child, -.table-bordered colgroup + thead tr:first-child th:last-child, -.table-bordered colgroup + tbody tr:first-child td:last-child { - -webkit-border-top-right-radius: 4px; - border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; -} - -.table-striped tbody > tr:nth-child(odd) > td, -.table-striped tbody > tr:nth-child(odd) > th { - background-color: #f9f9f9; -} - -.table-hover tbody tr:hover > td, -.table-hover tbody tr:hover > th { - background-color: #f5f5f5; -} - -table td[class*="span"], -table th[class*="span"], -.row-fluid table td[class*="span"], -.row-fluid table th[class*="span"] { - display: table-cell; - float: none; - margin-left: 0; -} - -.table td.span1, -.table th.span1 { - float: none; - width: 44px; - margin-left: 0; -} - -.table td.span2, -.table th.span2 { - float: none; - width: 124px; - margin-left: 0; -} - -.table td.span3, -.table th.span3 { - float: none; - width: 204px; - margin-left: 0; -} - -.table td.span4, -.table th.span4 { - float: none; - width: 284px; - margin-left: 0; -} - -.table td.span5, -.table th.span5 { - float: none; - width: 364px; - margin-left: 0; -} - -.table td.span6, -.table th.span6 { - float: none; - width: 444px; - margin-left: 0; -} - -.table td.span7, -.table th.span7 { - float: none; - width: 524px; - margin-left: 0; -} - -.table td.span8, -.table th.span8 { - float: none; - width: 604px; - margin-left: 0; -} - -.table td.span9, -.table th.span9 { - float: none; - width: 684px; - margin-left: 0; -} - -.table td.span10, -.table th.span10 { - float: none; - width: 764px; - margin-left: 0; -} - -.table td.span11, -.table th.span11 { - float: none; - width: 844px; - margin-left: 0; -} - -.table td.span12, -.table th.span12 { - float: none; - width: 924px; - margin-left: 0; -} - -.table tbody tr.success > td { - background-color: #dff0d8; -} - -.table tbody tr.error > td { - background-color: #f2dede; -} - -.table tbody tr.warning > td { - background-color: #fcf8e3; -} - -.table tbody tr.info > td { - background-color: #d9edf7; -} - -.table-hover tbody tr.success:hover > td { - background-color: #d0e9c6; -} - -.table-hover tbody tr.error:hover > td { - background-color: #ebcccc; -} - -.table-hover tbody tr.warning:hover > td { - background-color: #faf2cc; -} - -.table-hover tbody tr.info:hover > td { - background-color: #c4e3f3; -} - -[class^="icon-"], -[class*=" icon-"] { - display: inline-block; - width: 14px; - height: 14px; - margin-top: 1px; - *margin-right: .3em; - line-height: 14px; - vertical-align: text-top; - background-image: url("../img/glyphicons-halflings.png"); - background-position: 14px 14px; - background-repeat: no-repeat; -} - -/* White icons with optional class, or on hover/focus/active states of certain elements */ - -.icon-white, -.nav-pills > .active > a > [class^="icon-"], -.nav-pills > .active > a > [class*=" icon-"], -.nav-list > .active > a > [class^="icon-"], -.nav-list > .active > a > [class*=" icon-"], -.navbar-inverse .nav > .active > a > [class^="icon-"], -.navbar-inverse .nav > .active > a > [class*=" icon-"], -.dropdown-menu > li > a:hover > [class^="icon-"], -.dropdown-menu > li > a:focus > [class^="icon-"], -.dropdown-menu > li > a:hover > [class*=" icon-"], -.dropdown-menu > li > a:focus > [class*=" icon-"], -.dropdown-menu > .active > a > [class^="icon-"], -.dropdown-menu > .active > a > [class*=" icon-"], -.dropdown-submenu:hover > a > [class^="icon-"], -.dropdown-submenu:focus > a > [class^="icon-"], -.dropdown-submenu:hover > a > [class*=" icon-"], -.dropdown-submenu:focus > a > [class*=" icon-"] { - background-image: url("../img/glyphicons-halflings-white.png"); -} - -.icon-glass { - background-position: 0 0; -} - -.icon-music { - background-position: -24px 0; -} - -.icon-search { - background-position: -48px 0; -} - -.icon-envelope { - background-position: -72px 0; -} - -.icon-heart { - background-position: -96px 0; -} - -.icon-star { - background-position: -120px 0; -} - -.icon-star-empty { - background-position: -144px 0; -} - -.icon-user { - background-position: -168px 0; -} - -.icon-film { - background-position: -192px 0; -} - -.icon-th-large { - background-position: -216px 0; -} - -.icon-th { - background-position: -240px 0; -} - -.icon-th-list { - background-position: -264px 0; -} - -.icon-ok { - background-position: -288px 0; -} - -.icon-remove { - background-position: -312px 0; -} - -.icon-zoom-in { - background-position: -336px 0; -} - -.icon-zoom-out { - background-position: -360px 0; -} - -.icon-off { - background-position: -384px 0; -} - -.icon-signal { - background-position: -408px 0; -} - -.icon-cog { - background-position: -432px 0; -} - -.icon-trash { - background-position: -456px 0; -} - -.icon-home { - background-position: 0 -24px; -} - -.icon-file { - background-position: -24px -24px; -} - -.icon-time { - background-position: -48px -24px; -} - -.icon-road { - background-position: -72px -24px; -} - -.icon-download-alt { - background-position: -96px -24px; -} - -.icon-download { - background-position: -120px -24px; -} - -.icon-upload { - background-position: -144px -24px; -} - -.icon-inbox { - background-position: -168px -24px; -} - -.icon-play-circle { - background-position: -192px -24px; -} - -.icon-repeat { - background-position: -216px -24px; -} - -.icon-refresh { - background-position: -240px -24px; -} - -.icon-list-alt { - background-position: -264px -24px; -} - -.icon-lock { - background-position: -287px -24px; -} - -.icon-flag { - background-position: -312px -24px; -} - -.icon-headphones { - background-position: -336px -24px; -} - -.icon-volume-off { - background-position: -360px -24px; -} - -.icon-volume-down { - background-position: -384px -24px; -} - -.icon-volume-up { - background-position: -408px -24px; -} - -.icon-qrcode { - background-position: -432px -24px; -} - -.icon-barcode { - background-position: -456px -24px; -} - -.icon-tag { - background-position: 0 -48px; -} - -.icon-tags { - background-position: -25px -48px; -} - -.icon-book { - background-position: -48px -48px; -} - -.icon-bookmark { - background-position: -72px -48px; -} - -.icon-print { - background-position: -96px -48px; -} - -.icon-camera { - background-position: -120px -48px; -} - -.icon-font { - background-position: -144px -48px; -} - -.icon-bold { - background-position: -167px -48px; -} - -.icon-italic { - background-position: -192px -48px; -} - -.icon-text-height { - background-position: -216px -48px; -} - -.icon-text-width { - background-position: -240px -48px; -} - -.icon-align-left { - background-position: -264px -48px; -} - -.icon-align-center { - background-position: -288px -48px; -} - -.icon-align-right { - background-position: -312px -48px; -} - -.icon-align-justify { - background-position: -336px -48px; -} - -.icon-list { - background-position: -360px -48px; -} - -.icon-indent-left { - background-position: -384px -48px; -} - -.icon-indent-right { - background-position: -408px -48px; -} - -.icon-facetime-video { - background-position: -432px -48px; -} - -.icon-picture { - background-position: -456px -48px; -} - -.icon-pencil { - background-position: 0 -72px; -} - -.icon-map-marker { - background-position: -24px -72px; -} - -.icon-adjust { - background-position: -48px -72px; -} - -.icon-tint { - background-position: -72px -72px; -} - -.icon-edit { - background-position: -96px -72px; -} - -.icon-share { - background-position: -120px -72px; -} - -.icon-check { - background-position: -144px -72px; -} - -.icon-move { - background-position: -168px -72px; -} - -.icon-step-backward { - background-position: -192px -72px; -} - -.icon-fast-backward { - background-position: -216px -72px; -} - -.icon-backward { - background-position: -240px -72px; -} - -.icon-play { - background-position: -264px -72px; -} - -.icon-pause { - background-position: -288px -72px; -} - -.icon-stop { - background-position: -312px -72px; -} - -.icon-forward { - background-position: -336px -72px; -} - -.icon-fast-forward { - background-position: -360px -72px; -} - -.icon-step-forward { - background-position: -384px -72px; -} - -.icon-eject { - background-position: -408px -72px; -} - -.icon-chevron-left { - background-position: -432px -72px; -} - -.icon-chevron-right { - background-position: -456px -72px; -} - -.icon-plus-sign { - background-position: 0 -96px; -} - -.icon-minus-sign { - background-position: -24px -96px; -} - -.icon-remove-sign { - background-position: -48px -96px; -} - -.icon-ok-sign { - background-position: -72px -96px; -} - -.icon-question-sign { - background-position: -96px -96px; -} - -.icon-info-sign { - background-position: -120px -96px; -} - -.icon-screenshot { - background-position: -144px -96px; -} - -.icon-remove-circle { - background-position: -168px -96px; -} - -.icon-ok-circle { - background-position: -192px -96px; -} - -.icon-ban-circle { - background-position: -216px -96px; -} - -.icon-arrow-left { - background-position: -240px -96px; -} - -.icon-arrow-right { - background-position: -264px -96px; -} - -.icon-arrow-up { - background-position: -289px -96px; -} - -.icon-arrow-down { - background-position: -312px -96px; -} - -.icon-share-alt { - background-position: -336px -96px; -} - -.icon-resize-full { - background-position: -360px -96px; -} - -.icon-resize-small { - background-position: -384px -96px; -} - -.icon-plus { - background-position: -408px -96px; -} - -.icon-minus { - background-position: -433px -96px; -} - -.icon-asterisk { - background-position: -456px -96px; -} - -.icon-exclamation-sign { - background-position: 0 -120px; -} - -.icon-gift { - background-position: -24px -120px; -} - -.icon-leaf { - background-position: -48px -120px; -} - -.icon-fire { - background-position: -72px -120px; -} - -.icon-eye-open { - background-position: -96px -120px; -} - -.icon-eye-close { - background-position: -120px -120px; -} - -.icon-warning-sign { - background-position: -144px -120px; -} - -.icon-plane { - background-position: -168px -120px; -} - -.icon-calendar { - background-position: -192px -120px; -} - -.icon-random { - width: 16px; - background-position: -216px -120px; -} - -.icon-comment { - background-position: -240px -120px; -} - -.icon-magnet { - background-position: -264px -120px; -} - -.icon-chevron-up { - background-position: -288px -120px; -} - -.icon-chevron-down { - background-position: -313px -119px; -} - -.icon-retweet { - background-position: -336px -120px; -} - -.icon-shopping-cart { - background-position: -360px -120px; -} - -.icon-folder-close { - width: 16px; - background-position: -384px -120px; -} - -.icon-folder-open { - width: 16px; - background-position: -408px -120px; -} - -.icon-resize-vertical { - background-position: -432px -119px; -} - -.icon-resize-horizontal { - background-position: -456px -118px; -} - -.icon-hdd { - background-position: 0 -144px; -} - -.icon-bullhorn { - background-position: -24px -144px; -} - -.icon-bell { - background-position: -48px -144px; -} - -.icon-certificate { - background-position: -72px -144px; -} - -.icon-thumbs-up { - background-position: -96px -144px; -} - -.icon-thumbs-down { - background-position: -120px -144px; -} - -.icon-hand-right { - background-position: -144px -144px; -} - -.icon-hand-left { - background-position: -168px -144px; -} - -.icon-hand-up { - background-position: -192px -144px; -} - -.icon-hand-down { - background-position: -216px -144px; -} - -.icon-circle-arrow-right { - background-position: -240px -144px; -} - -.icon-circle-arrow-left { - background-position: -264px -144px; -} - -.icon-circle-arrow-up { - background-position: -288px -144px; -} - -.icon-circle-arrow-down { - background-position: -312px -144px; -} - -.icon-globe { - background-position: -336px -144px; -} - -.icon-wrench { - background-position: -360px -144px; -} - -.icon-tasks { - background-position: -384px -144px; -} - -.icon-filter { - background-position: -408px -144px; -} - -.icon-briefcase { - background-position: -432px -144px; -} - -.icon-fullscreen { - background-position: -456px -144px; -} - -.dropup, -.dropdown { - position: relative; -} - -.dropdown-toggle { - *margin-bottom: -3px; -} - -.dropdown-toggle:active, -.open .dropdown-toggle { - outline: 0; -} - -.caret { - display: inline-block; - width: 0; - height: 0; - vertical-align: top; - border-top: 4px solid #000000; - border-right: 4px solid transparent; - border-left: 4px solid transparent; - content: ""; -} - -.dropdown .caret { - margin-top: 8px; - margin-left: 2px; -} - -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - list-style: none; - background-color: #ffffff; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - *border-right-width: 2px; - *border-bottom-width: 2px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -webkit-background-clip: padding-box; - -moz-background-clip: padding; - background-clip: padding-box; -} - -.dropdown-menu.pull-right { - right: 0; - left: auto; -} - -.dropdown-menu .divider { - *width: 100%; - height: 1px; - margin: 9px 1px; - *margin: -5px 0 5px; - overflow: hidden; - background-color: #e5e5e5; - border-bottom: 1px solid #ffffff; -} - -.dropdown-menu > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 20px; - color: #333333; - white-space: nowrap; -} - -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus, -.dropdown-submenu:hover > a, -.dropdown-submenu:focus > a { - color: #ffffff; - text-decoration: none; - background-color: #0081c2; - background-image: -moz-linear-gradient(top, #0088cc, #0077b3); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); - background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); - background-image: -o-linear-gradient(top, #0088cc, #0077b3); - background-image: linear-gradient(to bottom, #0088cc, #0077b3); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); -} - -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - color: #ffffff; - text-decoration: none; - background-color: #0081c2; - background-image: -moz-linear-gradient(top, #0088cc, #0077b3); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); - background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); - background-image: -o-linear-gradient(top, #0088cc, #0077b3); - background-image: linear-gradient(to bottom, #0088cc, #0077b3); - background-repeat: repeat-x; - outline: 0; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); -} - -.dropdown-menu > .disabled > a, -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - color: #999999; -} - -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - text-decoration: none; - cursor: default; - background-color: transparent; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.open { - *z-index: 1000; -} - -.open > .dropdown-menu { - display: block; -} - -.dropdown-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 990; -} - -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} - -.dropup .caret, -.navbar-fixed-bottom .dropdown .caret { - border-top: 0; - border-bottom: 4px solid #000000; - content: ""; -} - -.dropup .dropdown-menu, -.navbar-fixed-bottom .dropdown .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 1px; -} - -.dropdown-submenu { - position: relative; -} - -.dropdown-submenu > .dropdown-menu { - top: 0; - left: 100%; - margin-top: -6px; - margin-left: -1px; - -webkit-border-radius: 0 6px 6px 6px; - -moz-border-radius: 0 6px 6px 6px; - border-radius: 0 6px 6px 6px; -} - -.dropdown-submenu:hover > .dropdown-menu { - display: block; -} - -.dropup .dropdown-submenu > .dropdown-menu { - top: auto; - bottom: 0; - margin-top: 0; - margin-bottom: -2px; - -webkit-border-radius: 5px 5px 5px 0; - -moz-border-radius: 5px 5px 5px 0; - border-radius: 5px 5px 5px 0; -} - -.dropdown-submenu > a:after { - display: block; - float: right; - width: 0; - height: 0; - margin-top: 5px; - margin-right: -10px; - border-color: transparent; - border-left-color: #cccccc; - border-style: solid; - border-width: 5px 0 5px 5px; - content: " "; -} - -.dropdown-submenu:hover > a:after { - border-left-color: #ffffff; -} - -.dropdown-submenu.pull-left { - float: none; -} - -.dropdown-submenu.pull-left > .dropdown-menu { - left: -100%; - margin-left: 10px; - -webkit-border-radius: 6px 0 6px 6px; - -moz-border-radius: 6px 0 6px 6px; - border-radius: 6px 0 6px 6px; -} - -.dropdown .dropdown-menu .nav-header { - padding-right: 20px; - padding-left: 20px; -} - -.typeahead { - z-index: 1051; - margin-top: 2px; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: #f5f5f5; - border: 1px solid #e3e3e3; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); -} - -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, 0.15); -} - -.well-large { - padding: 24px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} - -.well-small { - padding: 9px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -.fade { - opacity: 0; - -webkit-transition: opacity 0.15s linear; - -moz-transition: opacity 0.15s linear; - -o-transition: opacity 0.15s linear; - transition: opacity 0.15s linear; -} - -.fade.in { - opacity: 1; -} - -.collapse { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition: height 0.35s ease; - -moz-transition: height 0.35s ease; - -o-transition: height 0.35s ease; - transition: height 0.35s ease; -} - -.collapse.in { - height: auto; -} - -.close { - float: right; - font-size: 20px; - font-weight: bold; - line-height: 20px; - color: #000000; - text-shadow: 0 1px 0 #ffffff; - opacity: 0.2; - filter: alpha(opacity=20); -} - -.close:hover, -.close:focus { - color: #000000; - text-decoration: none; - cursor: pointer; - opacity: 0.4; - filter: alpha(opacity=40); -} - -button.close { - padding: 0; - cursor: pointer; - background: transparent; - border: 0; - -webkit-appearance: none; -} - -.btn { - display: inline-block; - *display: inline; - padding: 4px 12px; - margin-bottom: 0; - *margin-left: .3em; - font-size: 14px; - line-height: 20px; - color: #333333; - text-align: center; - text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); - vertical-align: middle; - cursor: pointer; - background-color: #f5f5f5; - *background-color: #e6e6e6; - background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); - background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); - background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); - background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); - background-repeat: repeat-x; - border: 1px solid #cccccc; - *border: 0; - border-color: #e6e6e6 #e6e6e6 #bfbfbf; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - border-bottom-color: #b3b3b3; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); - *zoom: 1; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -} - -.btn:hover, -.btn:focus, -.btn:active, -.btn.active, -.btn.disabled, -.btn[disabled] { - color: #333333; - background-color: #e6e6e6; - *background-color: #d9d9d9; -} - -.btn:active, -.btn.active { - background-color: #cccccc \9; -} - -.btn:first-child { - *margin-left: 0; -} - -.btn:hover, -.btn:focus { - color: #333333; - text-decoration: none; - background-position: 0 -15px; - -webkit-transition: background-position 0.1s linear; - -moz-transition: background-position 0.1s linear; - -o-transition: background-position 0.1s linear; - transition: background-position 0.1s linear; -} - -.btn:focus { - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} - -.btn.active, -.btn:active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); -} - -.btn.disabled, -.btn[disabled] { - cursor: default; - background-image: none; - opacity: 0.65; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; -} - -.btn-large { - padding: 11px 19px; - font-size: 17.5px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} - -.btn-large [class^="icon-"], -.btn-large [class*=" icon-"] { - margin-top: 4px; -} - -.btn-small { - padding: 2px 10px; - font-size: 11.9px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -.btn-small [class^="icon-"], -.btn-small [class*=" icon-"] { - margin-top: 0; -} - -.btn-mini [class^="icon-"], -.btn-mini [class*=" icon-"] { - margin-top: -1px; -} - -.btn-mini { - padding: 0 6px; - font-size: 10.5px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -.btn-block { - display: block; - width: 100%; - padding-right: 0; - padding-left: 0; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -.btn-block + .btn-block { - margin-top: 5px; -} - -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} - -.btn-primary.active, -.btn-warning.active, -.btn-danger.active, -.btn-success.active, -.btn-info.active, -.btn-inverse.active { - color: rgba(255, 255, 255, 0.75); -} - -.btn-primary { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #006dcc; - *background-color: #0044cc; - background-image: -moz-linear-gradient(top, #0088cc, #0044cc); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); - background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); - background-image: -o-linear-gradient(top, #0088cc, #0044cc); - background-image: linear-gradient(to bottom, #0088cc, #0044cc); - background-repeat: repeat-x; - border-color: #0044cc #0044cc #002a80; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.btn-primary:hover, -.btn-primary:focus, -.btn-primary:active, -.btn-primary.active, -.btn-primary.disabled, -.btn-primary[disabled] { - color: #ffffff; - background-color: #0044cc; - *background-color: #003bb3; -} - -.btn-primary:active, -.btn-primary.active { - background-color: #003399 \9; -} - -.btn-warning { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #faa732; - *background-color: #f89406; - background-image: -moz-linear-gradient(top, #fbb450, #f89406); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); - background-image: -webkit-linear-gradient(top, #fbb450, #f89406); - background-image: -o-linear-gradient(top, #fbb450, #f89406); - background-image: linear-gradient(to bottom, #fbb450, #f89406); - background-repeat: repeat-x; - border-color: #f89406 #f89406 #ad6704; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.btn-warning:hover, -.btn-warning:focus, -.btn-warning:active, -.btn-warning.active, -.btn-warning.disabled, -.btn-warning[disabled] { - color: #ffffff; - background-color: #f89406; - *background-color: #df8505; -} - -.btn-warning:active, -.btn-warning.active { - background-color: #c67605 \9; -} - -.btn-danger { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #da4f49; - *background-color: #bd362f; - background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); - background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); - background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); - background-image: linear-gradient(to bottom, #ee5f5b, #bd362f); - background-repeat: repeat-x; - border-color: #bd362f #bd362f #802420; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.btn-danger:hover, -.btn-danger:focus, -.btn-danger:active, -.btn-danger.active, -.btn-danger.disabled, -.btn-danger[disabled] { - color: #ffffff; - background-color: #bd362f; - *background-color: #a9302a; -} - -.btn-danger:active, -.btn-danger.active { - background-color: #942a25 \9; -} - -.btn-success { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #5bb75b; - *background-color: #51a351; - background-image: -moz-linear-gradient(top, #62c462, #51a351); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); - background-image: -webkit-linear-gradient(top, #62c462, #51a351); - background-image: -o-linear-gradient(top, #62c462, #51a351); - background-image: linear-gradient(to bottom, #62c462, #51a351); - background-repeat: repeat-x; - border-color: #51a351 #51a351 #387038; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.btn-success:hover, -.btn-success:focus, -.btn-success:active, -.btn-success.active, -.btn-success.disabled, -.btn-success[disabled] { - color: #ffffff; - background-color: #51a351; - *background-color: #499249; -} - -.btn-success:active, -.btn-success.active { - background-color: #408140 \9; -} - -.btn-info { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #49afcd; - *background-color: #2f96b4; - background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); - background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); - background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); - background-image: linear-gradient(to bottom, #5bc0de, #2f96b4); - background-repeat: repeat-x; - border-color: #2f96b4 #2f96b4 #1f6377; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.btn-info:hover, -.btn-info:focus, -.btn-info:active, -.btn-info.active, -.btn-info.disabled, -.btn-info[disabled] { - color: #ffffff; - background-color: #2f96b4; - *background-color: #2a85a0; -} - -.btn-info:active, -.btn-info.active { - background-color: #24748c \9; -} - -.btn-inverse { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #363636; - *background-color: #222222; - background-image: -moz-linear-gradient(top, #444444, #222222); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); - background-image: -webkit-linear-gradient(top, #444444, #222222); - background-image: -o-linear-gradient(top, #444444, #222222); - background-image: linear-gradient(to bottom, #444444, #222222); - background-repeat: repeat-x; - border-color: #222222 #222222 #000000; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.btn-inverse:hover, -.btn-inverse:focus, -.btn-inverse:active, -.btn-inverse.active, -.btn-inverse.disabled, -.btn-inverse[disabled] { - color: #ffffff; - background-color: #222222; - *background-color: #151515; -} - -.btn-inverse:active, -.btn-inverse.active { - background-color: #080808 \9; -} - -button.btn, -input[type="submit"].btn { - *padding-top: 3px; - *padding-bottom: 3px; -} - -button.btn::-moz-focus-inner, -input[type="submit"].btn::-moz-focus-inner { - padding: 0; - border: 0; -} - -button.btn.btn-large, -input[type="submit"].btn.btn-large { - *padding-top: 7px; - *padding-bottom: 7px; -} - -button.btn.btn-small, -input[type="submit"].btn.btn-small { - *padding-top: 3px; - *padding-bottom: 3px; -} - -button.btn.btn-mini, -input[type="submit"].btn.btn-mini { - *padding-top: 1px; - *padding-bottom: 1px; -} - -.btn-link, -.btn-link:active, -.btn-link[disabled] { - background-color: transparent; - background-image: none; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; -} - -.btn-link { - color: #0088cc; - cursor: pointer; - border-color: transparent; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.btn-link:hover, -.btn-link:focus { - color: #005580; - text-decoration: underline; - background-color: transparent; -} - -.btn-link[disabled]:hover, -.btn-link[disabled]:focus { - color: #333333; - text-decoration: none; -} - -.btn-group { - position: relative; - display: inline-block; - *display: inline; - *margin-left: .3em; - font-size: 0; - white-space: nowrap; - vertical-align: middle; - *zoom: 1; -} - -.btn-group:first-child { - *margin-left: 0; -} - -.btn-group + .btn-group { - margin-left: 5px; -} - -.btn-toolbar { - margin-top: 10px; - margin-bottom: 10px; - font-size: 0; -} - -.btn-toolbar > .btn + .btn, -.btn-toolbar > .btn-group + .btn, -.btn-toolbar > .btn + .btn-group { - margin-left: 5px; -} - -.btn-group > .btn { - position: relative; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.btn-group > .btn + .btn { - margin-left: -1px; -} - -.btn-group > .btn, -.btn-group > .dropdown-menu, -.btn-group > .popover { - font-size: 14px; -} - -.btn-group > .btn-mini { - font-size: 10.5px; -} - -.btn-group > .btn-small { - font-size: 11.9px; -} - -.btn-group > .btn-large { - font-size: 17.5px; -} - -.btn-group > .btn:first-child { - margin-left: 0; - -webkit-border-bottom-left-radius: 4px; - border-bottom-left-radius: 4px; - -webkit-border-top-left-radius: 4px; - border-top-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - -moz-border-radius-topleft: 4px; -} - -.btn-group > .btn:last-child, -.btn-group > .dropdown-toggle { - -webkit-border-top-right-radius: 4px; - border-top-right-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - border-bottom-right-radius: 4px; - -moz-border-radius-topright: 4px; - -moz-border-radius-bottomright: 4px; -} - -.btn-group > .btn.large:first-child { - margin-left: 0; - -webkit-border-bottom-left-radius: 6px; - border-bottom-left-radius: 6px; - -webkit-border-top-left-radius: 6px; - border-top-left-radius: 6px; - -moz-border-radius-bottomleft: 6px; - -moz-border-radius-topleft: 6px; -} - -.btn-group > .btn.large:last-child, -.btn-group > .large.dropdown-toggle { - -webkit-border-top-right-radius: 6px; - border-top-right-radius: 6px; - -webkit-border-bottom-right-radius: 6px; - border-bottom-right-radius: 6px; - -moz-border-radius-topright: 6px; - -moz-border-radius-bottomright: 6px; -} - -.btn-group > .btn:hover, -.btn-group > .btn:focus, -.btn-group > .btn:active, -.btn-group > .btn.active { - z-index: 2; -} - -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} - -.btn-group > .btn + .dropdown-toggle { - *padding-top: 5px; - padding-right: 8px; - *padding-bottom: 5px; - padding-left: 8px; - -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -} - -.btn-group > .btn-mini + .dropdown-toggle { - *padding-top: 2px; - padding-right: 5px; - *padding-bottom: 2px; - padding-left: 5px; -} - -.btn-group > .btn-small + .dropdown-toggle { - *padding-top: 5px; - *padding-bottom: 4px; -} - -.btn-group > .btn-large + .dropdown-toggle { - *padding-top: 7px; - padding-right: 12px; - *padding-bottom: 7px; - padding-left: 12px; -} - -.btn-group.open .dropdown-toggle { - background-image: none; - -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); -} - -.btn-group.open .btn.dropdown-toggle { - background-color: #e6e6e6; -} - -.btn-group.open .btn-primary.dropdown-toggle { - background-color: #0044cc; -} - -.btn-group.open .btn-warning.dropdown-toggle { - background-color: #f89406; -} - -.btn-group.open .btn-danger.dropdown-toggle { - background-color: #bd362f; -} - -.btn-group.open .btn-success.dropdown-toggle { - background-color: #51a351; -} - -.btn-group.open .btn-info.dropdown-toggle { - background-color: #2f96b4; -} - -.btn-group.open .btn-inverse.dropdown-toggle { - background-color: #222222; -} - -.btn .caret { - margin-top: 8px; - margin-left: 0; -} - -.btn-large .caret { - margin-top: 6px; -} - -.btn-large .caret { - border-top-width: 5px; - border-right-width: 5px; - border-left-width: 5px; -} - -.btn-mini .caret, -.btn-small .caret { - margin-top: 8px; -} - -.dropup .btn-large .caret { - border-bottom-width: 5px; -} - -.btn-primary .caret, -.btn-warning .caret, -.btn-danger .caret, -.btn-info .caret, -.btn-success .caret, -.btn-inverse .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; -} - -.btn-group-vertical { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - - *zoom: 1; -} - -.btn-group-vertical > .btn { - display: block; - float: none; - max-width: 100%; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.btn-group-vertical > .btn + .btn { - margin-top: -1px; - margin-left: 0; -} - -.btn-group-vertical > .btn:first-child { - -webkit-border-radius: 4px 4px 0 0; - -moz-border-radius: 4px 4px 0 0; - border-radius: 4px 4px 0 0; -} - -.btn-group-vertical > .btn:last-child { - -webkit-border-radius: 0 0 4px 4px; - -moz-border-radius: 0 0 4px 4px; - border-radius: 0 0 4px 4px; -} - -.btn-group-vertical > .btn-large:first-child { - -webkit-border-radius: 6px 6px 0 0; - -moz-border-radius: 6px 6px 0 0; - border-radius: 6px 6px 0 0; -} - -.btn-group-vertical > .btn-large:last-child { - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; -} - -.alert { - padding: 8px 35px 8px 14px; - margin-bottom: 20px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - background-color: #fcf8e3; - border: 1px solid #fbeed5; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -.alert, -.alert h4 { - color: #c09853; -} - -.alert h4 { - margin: 0; -} - -.alert .close { - position: relative; - top: -2px; - right: -21px; - line-height: 20px; -} - -.alert-success { - color: #468847; - background-color: #dff0d8; - border-color: #d6e9c6; -} - -.alert-success h4 { - color: #468847; -} - -.alert-danger, -.alert-error { - color: #b94a48; - background-color: #f2dede; - border-color: #eed3d7; -} - -.alert-danger h4, -.alert-error h4 { - color: #b94a48; -} - -.alert-info { - color: #3a87ad; - background-color: #d9edf7; - border-color: #bce8f1; -} - -.alert-info h4 { - color: #3a87ad; -} - -.alert-block { - padding-top: 14px; - padding-bottom: 14px; -} - -.alert-block > p, -.alert-block > ul { - margin-bottom: 0; -} - -.alert-block p + p { - margin-top: 5px; -} - -.nav { - margin-bottom: 20px; - margin-left: 0; - list-style: none; -} - -.nav > li > a { - display: block; -} - -.nav > li > a:hover, -.nav > li > a:focus { - text-decoration: none; - background-color: #eeeeee; -} - -.nav > li > a > img { - max-width: none; -} - -.nav > .pull-right { - float: right; -} - -.nav-header { - display: block; - padding: 3px 15px; - font-size: 11px; - font-weight: bold; - line-height: 20px; - color: #999999; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-transform: uppercase; -} - -.nav li + .nav-header { - margin-top: 9px; -} - -.nav-list { - padding-right: 15px; - padding-left: 15px; - margin-bottom: 0; -} - -.nav-list > li > a, -.nav-list .nav-header { - margin-right: -15px; - margin-left: -15px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); -} - -.nav-list > li > a { - padding: 3px 15px; -} - -.nav-list > .active > a, -.nav-list > .active > a:hover, -.nav-list > .active > a:focus { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); - background-color: #0088cc; -} - -.nav-list [class^="icon-"], -.nav-list [class*=" icon-"] { - margin-right: 2px; -} - -.nav-list .divider { - *width: 100%; - height: 1px; - margin: 9px 1px; - *margin: -5px 0 5px; - overflow: hidden; - background-color: #e5e5e5; - border-bottom: 1px solid #ffffff; -} - -.nav-tabs, -.nav-pills { - *zoom: 1; -} - -.nav-tabs:before, -.nav-pills:before, -.nav-tabs:after, -.nav-pills:after { - display: table; - line-height: 0; - content: ""; -} - -.nav-tabs:after, -.nav-pills:after { - clear: both; -} - -.nav-tabs > li, -.nav-pills > li { - float: left; -} - -.nav-tabs > li > a, -.nav-pills > li > a { - padding-right: 12px; - padding-left: 12px; - margin-right: 2px; - line-height: 14px; -} - -.nav-tabs { - border-bottom: 1px solid #ddd; -} - -.nav-tabs > li { - margin-bottom: -1px; -} - -.nav-tabs > li > a { - padding-top: 8px; - padding-bottom: 8px; - line-height: 20px; - border: 1px solid transparent; - -webkit-border-radius: 4px 4px 0 0; - -moz-border-radius: 4px 4px 0 0; - border-radius: 4px 4px 0 0; -} - -.nav-tabs > li > a:hover, -.nav-tabs > li > a:focus { - border-color: #eeeeee #eeeeee #dddddd; -} - -.nav-tabs > .active > a, -.nav-tabs > .active > a:hover, -.nav-tabs > .active > a:focus { - color: #555555; - cursor: default; - background-color: #ffffff; - border: 1px solid #ddd; - border-bottom-color: transparent; -} - -.nav-pills > li > a { - padding-top: 8px; - padding-bottom: 8px; - margin-top: 2px; - margin-bottom: 2px; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; -} - -.nav-pills > .active > a, -.nav-pills > .active > a:hover, -.nav-pills > .active > a:focus { - color: #ffffff; - background-color: #0088cc; -} - -.nav-stacked > li { - float: none; -} - -.nav-stacked > li > a { - margin-right: 0; -} - -.nav-tabs.nav-stacked { - border-bottom: 0; -} - -.nav-tabs.nav-stacked > li > a { - border: 1px solid #ddd; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.nav-tabs.nav-stacked > li:first-child > a { - -webkit-border-top-right-radius: 4px; - border-top-right-radius: 4px; - -webkit-border-top-left-radius: 4px; - border-top-left-radius: 4px; - -moz-border-radius-topright: 4px; - -moz-border-radius-topleft: 4px; -} - -.nav-tabs.nav-stacked > li:last-child > a { - -webkit-border-bottom-right-radius: 4px; - border-bottom-right-radius: 4px; - -webkit-border-bottom-left-radius: 4px; - border-bottom-left-radius: 4px; - -moz-border-radius-bottomright: 4px; - -moz-border-radius-bottomleft: 4px; -} - -.nav-tabs.nav-stacked > li > a:hover, -.nav-tabs.nav-stacked > li > a:focus { - z-index: 2; - border-color: #ddd; -} - -.nav-pills.nav-stacked > li > a { - margin-bottom: 3px; -} - -.nav-pills.nav-stacked > li:last-child > a { - margin-bottom: 1px; -} - -.nav-tabs .dropdown-menu { - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; -} - -.nav-pills .dropdown-menu { - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} - -.nav .dropdown-toggle .caret { - margin-top: 6px; - border-top-color: #0088cc; - border-bottom-color: #0088cc; -} - -.nav .dropdown-toggle:hover .caret, -.nav .dropdown-toggle:focus .caret { - border-top-color: #005580; - border-bottom-color: #005580; -} - -/* move down carets for tabs */ - -.nav-tabs .dropdown-toggle .caret { - margin-top: 8px; -} - -.nav .active .dropdown-toggle .caret { - border-top-color: #fff; - border-bottom-color: #fff; -} - -.nav-tabs .active .dropdown-toggle .caret { - border-top-color: #555555; - border-bottom-color: #555555; -} - -.nav > .dropdown.active > a:hover, -.nav > .dropdown.active > a:focus { - cursor: pointer; -} - -.nav-tabs .open .dropdown-toggle, -.nav-pills .open .dropdown-toggle, -.nav > li.dropdown.open.active > a:hover, -.nav > li.dropdown.open.active > a:focus { - color: #ffffff; - background-color: #999999; - border-color: #999999; -} - -.nav li.dropdown.open .caret, -.nav li.dropdown.open.active .caret, -.nav li.dropdown.open a:hover .caret, -.nav li.dropdown.open a:focus .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; - opacity: 1; - filter: alpha(opacity=100); -} - -.tabs-stacked .open > a:hover, -.tabs-stacked .open > a:focus { - border-color: #999999; -} - -.tabbable { - *zoom: 1; -} - -.tabbable:before, -.tabbable:after { - display: table; - line-height: 0; - content: ""; -} - -.tabbable:after { - clear: both; -} - -.tab-content { - overflow: auto; -} - -.tabs-below > .nav-tabs, -.tabs-right > .nav-tabs, -.tabs-left > .nav-tabs { - border-bottom: 0; -} - -.tab-content > .tab-pane, -.pill-content > .pill-pane { - display: none; -} - -.tab-content > .active, -.pill-content > .active { - display: block; -} - -.tabs-below > .nav-tabs { - border-top: 1px solid #ddd; -} - -.tabs-below > .nav-tabs > li { - margin-top: -1px; - margin-bottom: 0; -} - -.tabs-below > .nav-tabs > li > a { - -webkit-border-radius: 0 0 4px 4px; - -moz-border-radius: 0 0 4px 4px; - border-radius: 0 0 4px 4px; -} - -.tabs-below > .nav-tabs > li > a:hover, -.tabs-below > .nav-tabs > li > a:focus { - border-top-color: #ddd; - border-bottom-color: transparent; -} - -.tabs-below > .nav-tabs > .active > a, -.tabs-below > .nav-tabs > .active > a:hover, -.tabs-below > .nav-tabs > .active > a:focus { - border-color: transparent #ddd #ddd #ddd; -} - -.tabs-left > .nav-tabs > li, -.tabs-right > .nav-tabs > li { - float: none; -} - -.tabs-left > .nav-tabs > li > a, -.tabs-right > .nav-tabs > li > a { - min-width: 74px; - margin-right: 0; - margin-bottom: 3px; -} - -.tabs-left > .nav-tabs { - float: left; - margin-right: 19px; - border-right: 1px solid #ddd; -} - -.tabs-left > .nav-tabs > li > a { - margin-right: -1px; - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} - -.tabs-left > .nav-tabs > li > a:hover, -.tabs-left > .nav-tabs > li > a:focus { - border-color: #eeeeee #dddddd #eeeeee #eeeeee; -} - -.tabs-left > .nav-tabs .active > a, -.tabs-left > .nav-tabs .active > a:hover, -.tabs-left > .nav-tabs .active > a:focus { - border-color: #ddd transparent #ddd #ddd; - *border-right-color: #ffffff; -} - -.tabs-right > .nav-tabs { - float: right; - margin-left: 19px; - border-left: 1px solid #ddd; -} - -.tabs-right > .nav-tabs > li > a { - margin-left: -1px; - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} - -.tabs-right > .nav-tabs > li > a:hover, -.tabs-right > .nav-tabs > li > a:focus { - border-color: #eeeeee #eeeeee #eeeeee #dddddd; -} - -.tabs-right > .nav-tabs .active > a, -.tabs-right > .nav-tabs .active > a:hover, -.tabs-right > .nav-tabs .active > a:focus { - border-color: #ddd #ddd #ddd transparent; - *border-left-color: #ffffff; -} - -.nav > .disabled > a { - color: #999999; -} - -.nav > .disabled > a:hover, -.nav > .disabled > a:focus { - text-decoration: none; - cursor: default; - background-color: transparent; -} - -.navbar { - *position: relative; - *z-index: 2; - margin-bottom: 20px; - overflow: visible; -} - -.navbar-inner { - min-height: 40px; - padding-right: 20px; - padding-left: 20px; - background-color: #fafafa; - background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2)); - background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2); - background-image: -o-linear-gradient(top, #ffffff, #f2f2f2); - background-image: linear-gradient(to bottom, #ffffff, #f2f2f2); - background-repeat: repeat-x; - border: 1px solid #d4d4d4; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0); - *zoom: 1; - -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); - -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); -} - -.navbar-inner:before, -.navbar-inner:after { - display: table; - line-height: 0; - content: ""; -} - -.navbar-inner:after { - clear: both; -} - -.navbar .container { - width: auto; -} - -.nav-collapse.collapse { - height: auto; - overflow: visible; -} - -.navbar .brand { - display: block; - float: left; - padding: 10px 20px 10px; - margin-left: -20px; - font-size: 20px; - font-weight: 200; - color: #777777; - text-shadow: 0 1px 0 #ffffff; -} - -.navbar .brand:hover, -.navbar .brand:focus { - text-decoration: none; -} - -.navbar-text { - margin-bottom: 0; - line-height: 40px; - color: #777777; -} - -.navbar-link { - color: #777777; -} - -.navbar-link:hover, -.navbar-link:focus { - color: #333333; -} - -.navbar .divider-vertical { - height: 40px; - margin: 0 9px; - border-right: 1px solid #ffffff; - border-left: 1px solid #f2f2f2; -} - -.navbar .btn, -.navbar .btn-group { - margin-top: 5px; -} - -.navbar .btn-group .btn, -.navbar .input-prepend .btn, -.navbar .input-append .btn, -.navbar .input-prepend .btn-group, -.navbar .input-append .btn-group { - margin-top: 0; -} - -.navbar-form { - margin-bottom: 0; - *zoom: 1; -} - -.navbar-form:before, -.navbar-form:after { - display: table; - line-height: 0; - content: ""; -} - -.navbar-form:after { - clear: both; -} - -.navbar-form input, -.navbar-form select, -.navbar-form .radio, -.navbar-form .checkbox { - margin-top: 5px; -} - -.navbar-form input, -.navbar-form select, -.navbar-form .btn { - display: inline-block; - margin-bottom: 0; -} - -.navbar-form input[type="image"], -.navbar-form input[type="checkbox"], -.navbar-form input[type="radio"] { - margin-top: 3px; -} - -.navbar-form .input-append, -.navbar-form .input-prepend { - margin-top: 5px; - white-space: nowrap; -} - -.navbar-form .input-append input, -.navbar-form .input-prepend input { - margin-top: 0; -} - -.navbar-search { - position: relative; - float: left; - margin-top: 5px; - margin-bottom: 0; -} - -.navbar-search .search-query { - padding: 4px 14px; - margin-bottom: 0; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 13px; - font-weight: normal; - line-height: 1; - -webkit-border-radius: 15px; - -moz-border-radius: 15px; - border-radius: 15px; -} - -.navbar-static-top { - position: static; - margin-bottom: 0; -} - -.navbar-static-top .navbar-inner { - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.navbar-fixed-top, -.navbar-fixed-bottom { - position: fixed; - right: 0; - left: 0; - z-index: 1030; - margin-bottom: 0; -} - -.navbar-fixed-top .navbar-inner, -.navbar-static-top .navbar-inner { - border-width: 0 0 1px; -} - -.navbar-fixed-bottom .navbar-inner { - border-width: 1px 0 0; -} - -.navbar-fixed-top .navbar-inner, -.navbar-fixed-bottom .navbar-inner { - padding-right: 0; - padding-left: 0; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} - -.navbar-static-top .container, -.navbar-fixed-top .container, -.navbar-fixed-bottom .container { - width: 940px; -} - -.navbar-fixed-top { - top: 0; -} - -.navbar-fixed-top .navbar-inner, -.navbar-static-top .navbar-inner { - -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); - -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); - box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); -} - -.navbar-fixed-bottom { - bottom: 0; -} - -.navbar-fixed-bottom .navbar-inner { - -webkit-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); - -moz-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); - box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); -} - -.navbar .nav { - position: relative; - left: 0; - display: block; - float: left; - margin: 0 10px 0 0; -} - -.navbar .nav.pull-right { - float: right; - margin-right: 0; -} - -.navbar .nav > li { - float: left; -} - -.navbar .nav > li > a { - float: none; - padding: 10px 15px 10px; - color: #777777; - text-decoration: none; - text-shadow: 0 1px 0 #ffffff; -} - -.navbar .nav .dropdown-toggle .caret { - margin-top: 8px; -} - -.navbar .nav > li > a:focus, -.navbar .nav > li > a:hover { - color: #333333; - text-decoration: none; - background-color: transparent; -} - -.navbar .nav > .active > a, -.navbar .nav > .active > a:hover, -.navbar .nav > .active > a:focus { - color: #555555; - text-decoration: none; - background-color: #e5e5e5; - -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); - -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); - box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); -} - -.navbar .btn-navbar { - display: none; - float: right; - padding: 7px 10px; - margin-right: 5px; - margin-left: 5px; - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #ededed; - *background-color: #e5e5e5; - background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5)); - background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5); - background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5); - background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5); - background-repeat: repeat-x; - border-color: #e5e5e5 #e5e5e5 #bfbfbf; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); - -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); -} - -.navbar .btn-navbar:hover, -.navbar .btn-navbar:focus, -.navbar .btn-navbar:active, -.navbar .btn-navbar.active, -.navbar .btn-navbar.disabled, -.navbar .btn-navbar[disabled] { - color: #ffffff; - background-color: #e5e5e5; - *background-color: #d9d9d9; -} - -.navbar .btn-navbar:active, -.navbar .btn-navbar.active { - background-color: #cccccc \9; -} - -.navbar .btn-navbar .icon-bar { - display: block; - width: 18px; - height: 2px; - background-color: #f5f5f5; - -webkit-border-radius: 1px; - -moz-border-radius: 1px; - border-radius: 1px; - -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); - -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); -} - -.btn-navbar .icon-bar + .icon-bar { - margin-top: 3px; -} - -.navbar .nav > li > .dropdown-menu:before { - position: absolute; - top: -7px; - left: 9px; - display: inline-block; - border-right: 7px solid transparent; - border-bottom: 7px solid #ccc; - border-left: 7px solid transparent; - border-bottom-color: rgba(0, 0, 0, 0.2); - content: ''; -} - -.navbar .nav > li > .dropdown-menu:after { - position: absolute; - top: -6px; - left: 10px; - display: inline-block; - border-right: 6px solid transparent; - border-bottom: 6px solid #ffffff; - border-left: 6px solid transparent; - content: ''; -} - -.navbar-fixed-bottom .nav > li > .dropdown-menu:before { - top: auto; - bottom: -7px; - border-top: 7px solid #ccc; - border-bottom: 0; - border-top-color: rgba(0, 0, 0, 0.2); -} - -.navbar-fixed-bottom .nav > li > .dropdown-menu:after { - top: auto; - bottom: -6px; - border-top: 6px solid #ffffff; - border-bottom: 0; -} - -.navbar .nav li.dropdown > a:hover .caret, -.navbar .nav li.dropdown > a:focus .caret { - border-top-color: #333333; - border-bottom-color: #333333; -} - -.navbar .nav li.dropdown.open > .dropdown-toggle, -.navbar .nav li.dropdown.active > .dropdown-toggle, -.navbar .nav li.dropdown.open.active > .dropdown-toggle { - color: #555555; - background-color: #e5e5e5; -} - -.navbar .nav li.dropdown > .dropdown-toggle .caret { - border-top-color: #777777; - border-bottom-color: #777777; -} - -.navbar .nav li.dropdown.open > .dropdown-toggle .caret, -.navbar .nav li.dropdown.active > .dropdown-toggle .caret, -.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { - border-top-color: #555555; - border-bottom-color: #555555; -} - -.navbar .pull-right > li > .dropdown-menu, -.navbar .nav > li > .dropdown-menu.pull-right { - right: 0; - left: auto; -} - -.navbar .pull-right > li > .dropdown-menu:before, -.navbar .nav > li > .dropdown-menu.pull-right:before { - right: 12px; - left: auto; -} - -.navbar .pull-right > li > .dropdown-menu:after, -.navbar .nav > li > .dropdown-menu.pull-right:after { - right: 13px; - left: auto; -} - -.navbar .pull-right > li > .dropdown-menu .dropdown-menu, -.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu { - right: 100%; - left: auto; - margin-right: -1px; - margin-left: 0; - -webkit-border-radius: 6px 0 6px 6px; - -moz-border-radius: 6px 0 6px 6px; - border-radius: 6px 0 6px 6px; -} - -.navbar-inverse .navbar-inner { - background-color: #1b1b1b; - background-image: -moz-linear-gradient(top, #222222, #111111); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111)); - background-image: -webkit-linear-gradient(top, #222222, #111111); - background-image: -o-linear-gradient(top, #222222, #111111); - background-image: linear-gradient(to bottom, #222222, #111111); - background-repeat: repeat-x; - border-color: #252525; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0); -} - -.navbar-inverse .brand, -.navbar-inverse .nav > li > a { - color: #999999; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); -} - -.navbar-inverse .brand:hover, -.navbar-inverse .nav > li > a:hover, -.navbar-inverse .brand:focus, -.navbar-inverse .nav > li > a:focus { - color: #ffffff; -} - -.navbar-inverse .brand { - color: #999999; -} - -.navbar-inverse .navbar-text { - color: #999999; -} - -.navbar-inverse .nav > li > a:focus, -.navbar-inverse .nav > li > a:hover { - color: #ffffff; - background-color: transparent; -} - -.navbar-inverse .nav .active > a, -.navbar-inverse .nav .active > a:hover, -.navbar-inverse .nav .active > a:focus { - color: #ffffff; - background-color: #111111; -} - -.navbar-inverse .navbar-link { - color: #999999; -} - -.navbar-inverse .navbar-link:hover, -.navbar-inverse .navbar-link:focus { - color: #ffffff; -} - -.navbar-inverse .divider-vertical { - border-right-color: #222222; - border-left-color: #111111; -} - -.navbar-inverse .nav li.dropdown.open > .dropdown-toggle, -.navbar-inverse .nav li.dropdown.active > .dropdown-toggle, -.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle { - color: #ffffff; - background-color: #111111; -} - -.navbar-inverse .nav li.dropdown > a:hover .caret, -.navbar-inverse .nav li.dropdown > a:focus .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; -} - -.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { - border-top-color: #999999; - border-bottom-color: #999999; -} - -.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, -.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret, -.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; -} - -.navbar-inverse .navbar-search .search-query { - color: #ffffff; - background-color: #515151; - border-color: #111111; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); - -webkit-transition: none; - -moz-transition: none; - -o-transition: none; - transition: none; -} - -.navbar-inverse .navbar-search .search-query:-moz-placeholder { - color: #cccccc; -} - -.navbar-inverse .navbar-search .search-query:-ms-input-placeholder { - color: #cccccc; -} - -.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder { - color: #cccccc; -} - -.navbar-inverse .navbar-search .search-query:focus, -.navbar-inverse .navbar-search .search-query.focused { - padding: 5px 15px; - color: #333333; - text-shadow: 0 1px 0 #ffffff; - background-color: #ffffff; - border: 0; - outline: 0; - -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); - -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); - box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); -} - -.navbar-inverse .btn-navbar { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #0e0e0e; - *background-color: #040404; - background-image: -moz-linear-gradient(top, #151515, #040404); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404)); - background-image: -webkit-linear-gradient(top, #151515, #040404); - background-image: -o-linear-gradient(top, #151515, #040404); - background-image: linear-gradient(to bottom, #151515, #040404); - background-repeat: repeat-x; - border-color: #040404 #040404 #000000; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0); - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} - -.navbar-inverse .btn-navbar:hover, -.navbar-inverse .btn-navbar:focus, -.navbar-inverse .btn-navbar:active, -.navbar-inverse .btn-navbar.active, -.navbar-inverse .btn-navbar.disabled, -.navbar-inverse .btn-navbar[disabled] { - color: #ffffff; - background-color: #040404; - *background-color: #000000; -} - -.navbar-inverse .btn-navbar:active, -.navbar-inverse .btn-navbar.active { - background-color: #000000 \9; -} - -.breadcrumb { - padding: 8px 15px; - margin: 0 0 20px; - list-style: none; - background-color: #f5f5f5; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -.breadcrumb > li { - display: inline-block; - *display: inline; - text-shadow: 0 1px 0 #ffffff; - *zoom: 1; -} - -.breadcrumb > li > .divider { - padding: 0 5px; - color: #ccc; -} - -.breadcrumb > .active { - color: #999999; -} - -.pagination { - margin: 20px 0; -} - -.pagination ul { - display: inline-block; - *display: inline; - margin-bottom: 0; - margin-left: 0; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - *zoom: 1; - -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); -} - -.pagination ul > li { - display: inline; -} - -.pagination ul > li > a, -.pagination ul > li > span { - float: left; - padding: 4px 12px; - line-height: 20px; - text-decoration: none; - background-color: #ffffff; - border: 1px solid #dddddd; - border-left-width: 0; -} - -.pagination ul > li > a:hover, -.pagination ul > li > a:focus, -.pagination ul > .active > a, -.pagination ul > .active > span { - background-color: #f5f5f5; -} - -.pagination ul > .active > a, -.pagination ul > .active > span { - color: #999999; - cursor: default; -} - -.pagination ul > .disabled > span, -.pagination ul > .disabled > a, -.pagination ul > .disabled > a:hover, -.pagination ul > .disabled > a:focus { - color: #999999; - cursor: default; - background-color: transparent; -} - -.pagination ul > li:first-child > a, -.pagination ul > li:first-child > span { - border-left-width: 1px; - -webkit-border-bottom-left-radius: 4px; - border-bottom-left-radius: 4px; - -webkit-border-top-left-radius: 4px; - border-top-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - -moz-border-radius-topleft: 4px; -} - -.pagination ul > li:last-child > a, -.pagination ul > li:last-child > span { - -webkit-border-top-right-radius: 4px; - border-top-right-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - border-bottom-right-radius: 4px; - -moz-border-radius-topright: 4px; - -moz-border-radius-bottomright: 4px; -} - -.pagination-centered { - text-align: center; -} - -.pagination-right { - text-align: right; -} - -.pagination-large ul > li > a, -.pagination-large ul > li > span { - padding: 11px 19px; - font-size: 17.5px; -} - -.pagination-large ul > li:first-child > a, -.pagination-large ul > li:first-child > span { - -webkit-border-bottom-left-radius: 6px; - border-bottom-left-radius: 6px; - -webkit-border-top-left-radius: 6px; - border-top-left-radius: 6px; - -moz-border-radius-bottomleft: 6px; - -moz-border-radius-topleft: 6px; -} - -.pagination-large ul > li:last-child > a, -.pagination-large ul > li:last-child > span { - -webkit-border-top-right-radius: 6px; - border-top-right-radius: 6px; - -webkit-border-bottom-right-radius: 6px; - border-bottom-right-radius: 6px; - -moz-border-radius-topright: 6px; - -moz-border-radius-bottomright: 6px; -} - -.pagination-mini ul > li:first-child > a, -.pagination-small ul > li:first-child > a, -.pagination-mini ul > li:first-child > span, -.pagination-small ul > li:first-child > span { - -webkit-border-bottom-left-radius: 3px; - border-bottom-left-radius: 3px; - -webkit-border-top-left-radius: 3px; - border-top-left-radius: 3px; - -moz-border-radius-bottomleft: 3px; - -moz-border-radius-topleft: 3px; -} - -.pagination-mini ul > li:last-child > a, -.pagination-small ul > li:last-child > a, -.pagination-mini ul > li:last-child > span, -.pagination-small ul > li:last-child > span { - -webkit-border-top-right-radius: 3px; - border-top-right-radius: 3px; - -webkit-border-bottom-right-radius: 3px; - border-bottom-right-radius: 3px; - -moz-border-radius-topright: 3px; - -moz-border-radius-bottomright: 3px; -} - -.pagination-small ul > li > a, -.pagination-small ul > li > span { - padding: 2px 10px; - font-size: 11.9px; -} - -.pagination-mini ul > li > a, -.pagination-mini ul > li > span { - padding: 0 6px; - font-size: 10.5px; -} - -.pager { - margin: 20px 0; - text-align: center; - list-style: none; - *zoom: 1; -} - -.pager:before, -.pager:after { - display: table; - line-height: 0; - content: ""; -} - -.pager:after { - clear: both; -} - -.pager li { - display: inline; -} - -.pager li > a, -.pager li > span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - -webkit-border-radius: 15px; - -moz-border-radius: 15px; - border-radius: 15px; -} - -.pager li > a:hover, -.pager li > a:focus { - text-decoration: none; - background-color: #f5f5f5; -} - -.pager .next > a, -.pager .next > span { - float: right; -} - -.pager .previous > a, -.pager .previous > span { - float: left; -} - -.pager .disabled > a, -.pager .disabled > a:hover, -.pager .disabled > a:focus, -.pager .disabled > span { - color: #999999; - cursor: default; - background-color: #fff; -} - -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000000; -} - -.modal-backdrop.fade { - opacity: 0; -} - -.modal-backdrop, -.modal-backdrop.fade.in { - opacity: 0.8; - filter: alpha(opacity=80); -} - -.modal { - position: fixed; - top: 10%; - left: 50%; - z-index: 1050; - width: 560px; - margin-left: -280px; - background-color: #ffffff; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, 0.3); - *border: 1px solid #999; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - outline: none; - -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - -webkit-background-clip: padding-box; - -moz-background-clip: padding-box; - background-clip: padding-box; -} - -.modal.fade { - top: -25%; - -webkit-transition: opacity 0.3s linear, top 0.3s ease-out; - -moz-transition: opacity 0.3s linear, top 0.3s ease-out; - -o-transition: opacity 0.3s linear, top 0.3s ease-out; - transition: opacity 0.3s linear, top 0.3s ease-out; -} - -.modal.fade.in { - top: 10%; -} - -.modal-header { - padding: 9px 15px; - border-bottom: 1px solid #eee; -} - -.modal-header .close { - margin-top: 2px; -} - -.modal-header h3 { - margin: 0; - line-height: 30px; -} - -.modal-body { - position: relative; - max-height: 400px; - padding: 15px; - overflow-y: auto; -} - -.modal-form { - margin-bottom: 0; -} - -.modal-footer { - padding: 14px 15px 15px; - margin-bottom: 0; - text-align: right; - background-color: #f5f5f5; - border-top: 1px solid #ddd; - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; - *zoom: 1; - -webkit-box-shadow: inset 0 1px 0 #ffffff; - -moz-box-shadow: inset 0 1px 0 #ffffff; - box-shadow: inset 0 1px 0 #ffffff; -} - -.modal-footer:before, -.modal-footer:after { - display: table; - line-height: 0; - content: ""; -} - -.modal-footer:after { - clear: both; -} - -.modal-footer .btn + .btn { - margin-bottom: 0; - margin-left: 5px; -} - -.modal-footer .btn-group .btn + .btn { - margin-left: -1px; -} - -.modal-footer .btn-block + .btn-block { - margin-left: 0; -} - -.tooltip { - position: absolute; - z-index: 1030; - display: block; - font-size: 11px; - line-height: 1.4; - opacity: 0; - filter: alpha(opacity=0); - visibility: visible; -} - -.tooltip.in { - opacity: 0.8; - filter: alpha(opacity=80); -} - -.tooltip.top { - padding: 5px 0; - margin-top: -3px; -} - -.tooltip.right { - padding: 0 5px; - margin-left: 3px; -} - -.tooltip.bottom { - padding: 5px 0; - margin-top: 3px; -} - -.tooltip.left { - padding: 0 5px; - margin-left: -3px; -} - -.tooltip-inner { - max-width: 200px; - padding: 8px; - color: #ffffff; - text-align: center; - text-decoration: none; - background-color: #000000; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} - -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-top-color: #000000; - border-width: 5px 5px 0; -} - -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-right-color: #000000; - border-width: 5px 5px 5px 0; -} - -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-left-color: #000000; - border-width: 5px 0 5px 5px; -} - -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-bottom-color: #000000; - border-width: 0 5px 5px; -} - -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1010; - display: none; - max-width: 276px; - padding: 1px; - text-align: left; - white-space: normal; - background-color: #ffffff; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -webkit-background-clip: padding-box; - -moz-background-clip: padding; - background-clip: padding-box; -} - -.popover.top { - margin-top: -10px; -} - -.popover.right { - margin-left: 10px; -} - -.popover.bottom { - margin-top: 10px; -} - -.popover.left { - margin-left: -10px; -} - -.popover-title { - padding: 8px 14px; - margin: 0; - font-size: 14px; - font-weight: normal; - line-height: 18px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - -webkit-border-radius: 5px 5px 0 0; - -moz-border-radius: 5px 5px 0 0; - border-radius: 5px 5px 0 0; -} - -.popover-title:empty { - display: none; -} - -.popover-content { - padding: 9px 14px; -} - -.popover .arrow, -.popover .arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} - -.popover .arrow { - border-width: 11px; -} - -.popover .arrow:after { - border-width: 10px; - content: ""; -} - -.popover.top .arrow { - bottom: -11px; - left: 50%; - margin-left: -11px; - border-top-color: #999; - border-top-color: rgba(0, 0, 0, 0.25); - border-bottom-width: 0; -} - -.popover.top .arrow:after { - bottom: 1px; - margin-left: -10px; - border-top-color: #ffffff; - border-bottom-width: 0; -} - -.popover.right .arrow { - top: 50%; - left: -11px; - margin-top: -11px; - border-right-color: #999; - border-right-color: rgba(0, 0, 0, 0.25); - border-left-width: 0; -} - -.popover.right .arrow:after { - bottom: -10px; - left: 1px; - border-right-color: #ffffff; - border-left-width: 0; -} - -.popover.bottom .arrow { - top: -11px; - left: 50%; - margin-left: -11px; - border-bottom-color: #999; - border-bottom-color: rgba(0, 0, 0, 0.25); - border-top-width: 0; -} - -.popover.bottom .arrow:after { - top: 1px; - margin-left: -10px; - border-bottom-color: #ffffff; - border-top-width: 0; -} - -.popover.left .arrow { - top: 50%; - right: -11px; - margin-top: -11px; - border-left-color: #999; - border-left-color: rgba(0, 0, 0, 0.25); - border-right-width: 0; -} - -.popover.left .arrow:after { - right: 1px; - bottom: -10px; - border-left-color: #ffffff; - border-right-width: 0; -} - -.thumbnails { - margin-left: -20px; - list-style: none; - *zoom: 1; -} - -.thumbnails:before, -.thumbnails:after { - display: table; - line-height: 0; - content: ""; -} - -.thumbnails:after { - clear: both; -} - -.row-fluid .thumbnails { - margin-left: 0; -} - -.thumbnails > li { - float: left; - margin-bottom: 20px; - margin-left: 20px; -} - -.thumbnail { - display: block; - padding: 4px; - line-height: 20px; - border: 1px solid #ddd; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); - -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); - -webkit-transition: all 0.2s ease-in-out; - -moz-transition: all 0.2s ease-in-out; - -o-transition: all 0.2s ease-in-out; - transition: all 0.2s ease-in-out; -} - -a.thumbnail:hover, -a.thumbnail:focus { - border-color: #0088cc; - -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); - -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); - box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); -} - -.thumbnail > img { - display: block; - max-width: 100%; - margin-right: auto; - margin-left: auto; -} - -.thumbnail .caption { - padding: 9px; - color: #555555; -} - -.media, -.media-body { - overflow: hidden; - *overflow: visible; - zoom: 1; -} - -.media, -.media .media { - margin-top: 15px; -} - -.media:first-child { - margin-top: 0; -} - -.media-object { - display: block; -} - -.media-heading { - margin: 0 0 5px; -} - -.media > .pull-left { - margin-right: 10px; -} - -.media > .pull-right { - margin-left: 10px; -} - -.media-list { - margin-left: 0; - list-style: none; -} - -.label, -.badge { - display: inline-block; - padding: 2px 4px; - font-size: 11.844px; - font-weight: bold; - line-height: 14px; - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - white-space: nowrap; - vertical-align: baseline; - background-color: #999999; -} - -.label { - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -.badge { - padding-right: 9px; - padding-left: 9px; - -webkit-border-radius: 9px; - -moz-border-radius: 9px; - border-radius: 9px; -} - -.label:empty, -.badge:empty { - display: none; -} - -a.label:hover, -a.label:focus, -a.badge:hover, -a.badge:focus { - color: #ffffff; - text-decoration: none; - cursor: pointer; -} - -.label-important, -.badge-important { - background-color: #b94a48; -} - -.label-important[href], -.badge-important[href] { - background-color: #953b39; -} - -.label-warning, -.badge-warning { - background-color: #f89406; -} - -.label-warning[href], -.badge-warning[href] { - background-color: #c67605; -} - -.label-success, -.badge-success { - background-color: #468847; -} - -.label-success[href], -.badge-success[href] { - background-color: #356635; -} - -.label-info, -.badge-info { - background-color: #3a87ad; -} - -.label-info[href], -.badge-info[href] { - background-color: #2d6987; -} - -.label-inverse, -.badge-inverse { - background-color: #333333; -} - -.label-inverse[href], -.badge-inverse[href] { - background-color: #1a1a1a; -} - -.btn .label, -.btn .badge { - position: relative; - top: -1px; -} - -.btn-mini .label, -.btn-mini .badge { - top: 0; -} - -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} - -@-moz-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} - -@-ms-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} - -@-o-keyframes progress-bar-stripes { - from { - background-position: 0 0; - } - to { - background-position: 40px 0; - } -} - -@keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} - -.progress { - height: 20px; - margin-bottom: 20px; - overflow: hidden; - background-color: #f7f7f7; - background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); - background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); - background-repeat: repeat-x; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); -} - -.progress .bar { - float: left; - width: 0; - height: 100%; - font-size: 12px; - color: #ffffff; - text-align: center; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #0e90d2; - background-image: -moz-linear-gradient(top, #149bdf, #0480be); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); - background-image: -webkit-linear-gradient(top, #149bdf, #0480be); - background-image: -o-linear-gradient(top, #149bdf, #0480be); - background-image: linear-gradient(to bottom, #149bdf, #0480be); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - -webkit-transition: width 0.6s ease; - -moz-transition: width 0.6s ease; - -o-transition: width 0.6s ease; - transition: width 0.6s ease; -} - -.progress .bar + .bar { - -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); - box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); -} - -.progress-striped .bar { - background-color: #149bdf; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - -webkit-background-size: 40px 40px; - -moz-background-size: 40px 40px; - -o-background-size: 40px 40px; - background-size: 40px 40px; -} - -.progress.active .bar { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -moz-animation: progress-bar-stripes 2s linear infinite; - -ms-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} - -.progress-danger .bar, -.progress .bar-danger { - background-color: #dd514c; - background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); - background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); - background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); - background-image: linear-gradient(to bottom, #ee5f5b, #c43c35); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0); -} - -.progress-danger.progress-striped .bar, -.progress-striped .bar-danger { - background-color: #ee5f5b; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} - -.progress-success .bar, -.progress .bar-success { - background-color: #5eb95e; - background-image: -moz-linear-gradient(top, #62c462, #57a957); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); - background-image: -webkit-linear-gradient(top, #62c462, #57a957); - background-image: -o-linear-gradient(top, #62c462, #57a957); - background-image: linear-gradient(to bottom, #62c462, #57a957); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0); -} - -.progress-success.progress-striped .bar, -.progress-striped .bar-success { - background-color: #62c462; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} - -.progress-info .bar, -.progress .bar-info { - background-color: #4bb1cf; - background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); - background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); - background-image: -o-linear-gradient(top, #5bc0de, #339bb9); - background-image: linear-gradient(to bottom, #5bc0de, #339bb9); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0); -} - -.progress-info.progress-striped .bar, -.progress-striped .bar-info { - background-color: #5bc0de; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} - -.progress-warning .bar, -.progress .bar-warning { - background-color: #faa732; - background-image: -moz-linear-gradient(top, #fbb450, #f89406); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); - background-image: -webkit-linear-gradient(top, #fbb450, #f89406); - background-image: -o-linear-gradient(top, #fbb450, #f89406); - background-image: linear-gradient(to bottom, #fbb450, #f89406); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); -} - -.progress-warning.progress-striped .bar, -.progress-striped .bar-warning { - background-color: #fbb450; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} - -.accordion { - margin-bottom: 20px; -} - -.accordion-group { - margin-bottom: 2px; - border: 1px solid #e5e5e5; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} - -.accordion-heading { - border-bottom: 0; -} - -.accordion-heading .accordion-toggle { - display: block; - padding: 8px 15px; -} - -.accordion-toggle { - cursor: pointer; -} - -.accordion-inner { - padding: 9px 15px; - border-top: 1px solid #e5e5e5; -} - -.carousel { - position: relative; - margin-bottom: 20px; - line-height: 1; -} - -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} - -.carousel-inner > .item { - position: relative; - display: none; - -webkit-transition: 0.6s ease-in-out left; - -moz-transition: 0.6s ease-in-out left; - -o-transition: 0.6s ease-in-out left; - transition: 0.6s ease-in-out left; -} - -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - display: block; - line-height: 1; -} - -.carousel-inner > .active, -.carousel-inner > .next, -.carousel-inner > .prev { - display: block; -} - -.carousel-inner > .active { - left: 0; -} - -.carousel-inner > .next, -.carousel-inner > .prev { - position: absolute; - top: 0; - width: 100%; -} - -.carousel-inner > .next { - left: 100%; -} - -.carousel-inner > .prev { - left: -100%; -} - -.carousel-inner > .next.left, -.carousel-inner > .prev.right { - left: 0; -} - -.carousel-inner > .active.left { - left: -100%; -} - -.carousel-inner > .active.right { - left: 100%; -} - -.carousel-control { - position: absolute; - top: 40%; - left: 15px; - width: 40px; - height: 40px; - margin-top: -20px; - font-size: 60px; - font-weight: 100; - line-height: 30px; - color: #ffffff; - text-align: center; - background: #222222; - border: 3px solid #ffffff; - -webkit-border-radius: 23px; - -moz-border-radius: 23px; - border-radius: 23px; - opacity: 0.5; - filter: alpha(opacity=50); -} - -.carousel-control.right { - right: 15px; - left: auto; -} - -.carousel-control:hover, -.carousel-control:focus { - color: #ffffff; - text-decoration: none; - opacity: 0.9; - filter: alpha(opacity=90); -} - -.carousel-indicators { - position: absolute; - top: 15px; - right: 15px; - z-index: 5; - margin: 0; - list-style: none; -} - -.carousel-indicators li { - display: block; - float: left; - width: 10px; - height: 10px; - margin-left: 5px; - text-indent: -999px; - background-color: #ccc; - background-color: rgba(255, 255, 255, 0.25); - border-radius: 5px; -} - -.carousel-indicators .active { - background-color: #fff; -} - -.carousel-caption { - position: absolute; - right: 0; - bottom: 0; - left: 0; - padding: 15px; - background: #333333; - background: rgba(0, 0, 0, 0.75); -} - -.carousel-caption h4, -.carousel-caption p { - line-height: 20px; - color: #ffffff; -} - -.carousel-caption h4 { - margin: 0 0 5px; -} - -.carousel-caption p { - margin-bottom: 0; -} - -.hero-unit { - padding: 60px; - margin-bottom: 30px; - font-size: 18px; - font-weight: 200; - line-height: 30px; - color: inherit; - background-color: #eeeeee; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} - -.hero-unit h1 { - margin-bottom: 0; - font-size: 60px; - line-height: 1; - letter-spacing: -1px; - color: inherit; -} - -.hero-unit li { - line-height: 30px; -} - -.pull-right { - float: right; -} - -.pull-left { - float: left; -} - -.hide { - display: none; -} - -.show { - display: block; -} - -.invisible { - visibility: hidden; -} - -.affix { - position: fixed; -} diff --git a/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.min.css b/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.min.css deleted file mode 100644 index df96c86485..0000000000 --- a/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.min.css +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * Bootstrap v2.3.2 - * - * Copyright 2013 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world by @mdo and @fat. - */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover,a:focus{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#999}a.muted:hover,a.muted:focus{color:#808080}.text-warning{color:#c09853}a.text-warning:hover,a.text-warning:focus{color:#a47e3c}.text-error{color:#b94a48}a.text-error:hover,a.text-error:focus{color:#953b39}.text-info{color:#3a87ad}a.text-info:hover,a.text-info:focus{color:#2d6987}.text-success{color:#468847}a.text-success:hover,a.text-success:focus{color:#356635}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{line-height:40px}h1{font-size:38.5px}h2{font-size:31.5px}h3{font-size:24.5px}h4{font-size:17.5px}h5{font-size:14px}h6{font-size:11.9px}h1 small{font-size:24.5px}h2 small{font-size:17.5px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}ul.inline,ol.inline{margin-left:0;list-style:none}ul.inline>li,ol.inline>li{display:inline-block;*display:inline;padding-right:5px;padding-left:5px;*zoom:1}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:17.5px;font-weight:300;line-height:1.25}blockquote small{display:block;line-height:20px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#999;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#999}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.radio,.checkbox{min-height:20px;padding-left:20px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning .control-label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error .control-label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success .control-label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group.info .control-label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#595959}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{display:inline-block;margin-bottom:10px;font-size:0;white-space:nowrap;vertical-align:middle}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu,.input-append .popover,.input-prepend .popover{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input+.btn-group .btn:last-child,.input-append select+.btn-group .btn:last-child,.input-append .uneditable-input+.btn-group .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child,.input-append .btn-group:last-child>.dropdown-toggle{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child,.table-bordered tbody:first-child tr:first-child>th:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child,.table-bordered tbody:first-child tr:first-child>th:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tbody:last-child tr:last-child>th:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>th:first-child{-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tbody:last-child tr:last-child>th:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>th:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover tbody tr:hover>td,.table-hover tbody tr:hover>th{background-color:#f5f5f5}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:124px;margin-left:0}.table td.span3,.table th.span3{float:none;width:204px;margin-left:0}.table td.span4,.table th.span4{float:none;width:284px;margin-left:0}.table td.span5,.table th.span5{float:none;width:364px;margin-left:0}.table td.span6,.table th.span6{float:none;width:444px;margin-left:0}.table td.span7,.table th.span7{float:none;width:524px;margin-left:0}.table td.span8,.table th.span8{float:none;width:604px;margin-left:0}.table td.span9,.table th.span9{float:none;width:684px;margin-left:0}.table td.span10,.table th.span10{float:none;width:764px;margin-left:0}.table td.span11,.table th.span11{float:none;width:844px;margin-left:0}.table td.span12,.table th.span12{float:none;width:924px;margin-left:0}.table tbody tr.success>td{background-color:#dff0d8}.table tbody tr.error>td{background-color:#f2dede}.table tbody tr.warning>td{background-color:#fcf8e3}.table tbody tr.info>td{background-color:#d9edf7}.table-hover tbody tr.success:hover>td{background-color:#d0e9c6}.table-hover tbody tr.error:hover>td{background-color:#ebcccc}.table-hover tbody tr.warning:hover>td{background-color:#faf2cc}.table-hover tbody tr.info:hover>td{background-color:#c4e3f3}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:focus>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>li>a:focus>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:focus>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"],.dropdown-submenu:focus>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{width:16px;background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-submenu:hover>a,.dropdown-submenu:focus>a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open{*z-index:1000}.open>.dropdown-menu{display:block}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{z-index:1051;margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 12px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #ccc;*border:0;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:focus,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover,.btn:focus{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;*background-color:#04c;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary:active,.btn-primary.active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#08c;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover,.btn-link:focus{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus{color:#333;text-decoration:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:14px}.btn-group>.btn-mini{font-size:10.5px}.btn-group>.btn-small{font-size:11.9px}.btn-group>.btn-large{font-size:17.5px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical>.btn{display:block;float:none;max-width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical>.btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical>.btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical>.btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical>.btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert,.alert h4{color:#c09853}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success h4{color:#468847}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger h4,.alert-error h4{color:#b94a48}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info h4{color:#3a87ad}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li>a>img{max-width:none}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover,.nav-list>.active>a:focus{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover,.nav-tabs>li>a:focus{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover,.nav-tabs>.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover,.nav-pills>.active>a:focus{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover,.nav-tabs.nav-stacked>li>a:focus{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav .dropdown-toggle:hover .caret,.nav .dropdown-toggle:focus .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover,.nav>.dropdown.active>a:focus{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover,.nav>li.dropdown.open.active>a:focus{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret,.nav li.dropdown.open a:focus .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover,.tabs-stacked .open>a:focus{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover,.tabs-below>.nav-tabs>li>a:focus{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover,.tabs-below>.nav-tabs>.active>a:focus{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover,.tabs-left>.nav-tabs>li>a:focus{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover,.tabs-left>.nav-tabs .active>a:focus{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover,.tabs-right>.nav-tabs>li>a:focus{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover,.tabs-right>.nav-tabs .active>a:focus{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#999}.nav>.disabled>a:hover,.nav>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover,.navbar .brand:focus{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px;color:#777}.navbar-link{color:#777}.navbar-link:hover,.navbar-link:focus{color:#333}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn,.navbar .input-prepend .btn-group,.navbar .input-append .btn-group{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:5px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#333;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:focus,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown>a:hover .caret,.navbar .nav li.dropdown>a:focus .caret{border-top-color:#333;border-bottom-color:#333}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover,.navbar-inverse .brand:focus,.navbar-inverse .nav>li>a:focus{color:#fff}.navbar-inverse .brand{color:#999}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover,.navbar-inverse .navbar-link:focus{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>a:hover .caret,.navbar-inverse .nav li.dropdown>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:-moz-linear-gradient(top,#151515,#040404);background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:focus,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb>li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb>li>.divider{padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>li>a:focus,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#999;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover,.pagination ul>.disabled>a:focus{color:#999;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;cursor:default;background-color:#fff}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;outline:0;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:10%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{position:relative;max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.tooltip{position:absolute;z-index:1030;display:block;font-size:11px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-title:empty{display:none}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover,a.thumbnail:focus{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding-right:9px;padding-left:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}.label:empty,.badge:empty{display:none}a.label:hover,a.label:focus,a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-indicators{position:absolute;top:15px;right:15px;z-index:5;margin:0;list-style:none}.carousel-indicators li{display:block;float:left;width:10px;height:10px;margin-left:5px;text-indent:-999px;background-color:#ccc;background-color:rgba(255,255,255,0.25);border-radius:5px}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:30px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed} diff --git a/pom.xml b/pom.xml index 1ca04c47d3..5d1880767b 100644 --- a/pom.xml +++ b/pom.xml @@ -139,6 +139,18 @@ maven-project-info-reports-plugin 2.9 + + org.lesscss + lesscss-maven-plugin + 1.7.0.1.1 + + + + compile + + + + From e1ae8f3d8da6efa0e1c03632d9c8e9117a42dfcd Mon Sep 17 00:00:00 2001 From: Ryan Pickett Date: Fri, 17 Feb 2017 14:43:52 +0000 Subject: [PATCH 136/269] Add parameters from error response to exception Introduce a new exception class to hold the parameters from an authentication error response, allowing simpler retrieval later in the filter processing. --- .../AuthorizationEndpointException.java | 33 ++++++++++++++ .../client/OIDCAuthenticationFilter.java | 2 +- .../client/TestOIDCAuthenticationFilter.java | 44 +++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java create mode 100644 openid-connect-client/src/test/java/org/mitre/openid/connect/client/TestOIDCAuthenticationFilter.java diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java new file mode 100644 index 0000000000..6d7b8c11b5 --- /dev/null +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java @@ -0,0 +1,33 @@ +package org.mitre.openid.connect.client; + +import org.springframework.security.authentication.AuthenticationServiceException; + +public class AuthorizationEndpointException extends AuthenticationServiceException { + + private static final long serialVersionUID = 6953119789654778380L; + + private String error; + + private String errorDescription; + + private String errorURI; + + public AuthorizationEndpointException(String error, String errorDescription, String errorURI) { + super("Error from Authorization Endpoint: " + error + " " + errorDescription + " " + errorURI); + this.error = error; + this.errorDescription = errorDescription; + this.errorURI = errorURI; + } + + public String getError() { + return error; + } + + public String getErrorDescription() { + return errorDescription; + } + + public String getErrorURI() { + return errorURI; + } +} diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java index d6ce542fd5..6a52b3f3c3 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java @@ -649,7 +649,7 @@ protected void handleError(HttpServletRequest request, HttpServletResponse respo String errorDescription = request.getParameter("error_description"); String errorURI = request.getParameter("error_uri"); - throw new AuthenticationServiceException("Error from Authorization Endpoint: " + error + " " + errorDescription + " " + errorURI); + throw new AuthorizationEndpointException(error, errorDescription, errorURI); } /** diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/TestOIDCAuthenticationFilter.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/TestOIDCAuthenticationFilter.java new file mode 100644 index 0000000000..2918e11896 --- /dev/null +++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/TestOIDCAuthenticationFilter.java @@ -0,0 +1,44 @@ +package org.mitre.openid.connect.client; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.security.authentication.AuthenticationServiceException; + +public class TestOIDCAuthenticationFilter { + + private OIDCAuthenticationFilter filter = new OIDCAuthenticationFilter(); + + @Test + public void attemptAuthentication_error() throws Exception { + + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + Mockito.when(request.getParameter("error")).thenReturn("Error"); + Mockito.when(request.getParameter("error_description")).thenReturn("Description"); + Mockito.when(request.getParameter("error_uri")).thenReturn("http://example.com"); + + try { + filter.attemptAuthentication(request, mock(HttpServletResponse.class)); + + fail("AuthorizationEndpointException expected."); + } + catch (AuthorizationEndpointException exception) { + assertThat(exception.getMessage(), + is("Error from Authorization Endpoint: Error Description http://example.com")); + + assertThat(exception.getError(), is("Error")); + assertThat(exception.getErrorDescription(), is("Description")); + assertThat(exception.getErrorURI(), is("http://example.com")); + + assertThat(exception, is(instanceOf(AuthenticationServiceException.class))); + } + } +} From 1cb5b6c6ffdf36cb4769be8e61c6661939860e3a Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 3 Mar 2017 17:23:39 -0500 Subject: [PATCH 137/269] added toString to error for debug and display --- .../connect/client/AuthorizationEndpointException.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java index 6d7b8c11b5..be017f4739 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java @@ -30,4 +30,12 @@ public String getErrorDescription() { public String getErrorURI() { return errorURI; } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "AuthorizationEndpointException [error=" + error + ", errorDescription=" + errorDescription + ", errorURI=" + errorURI + "]"; + } } From 0d84db49af7d9f71faa0aa157ce841c4ba70d537 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 3 Mar 2017 18:03:20 -0500 Subject: [PATCH 138/269] [maven-release-plugin] prepare release mitreid-connect-1.3.0-RC1 --- openid-connect-client/pom.xml | 2 +- openid-connect-common/pom.xml | 2 +- openid-connect-server-webapp/pom.xml | 2 +- openid-connect-server/pom.xml | 2 +- pom.xml | 5 ++--- uma-server-webapp/pom.xml | 2 +- uma-server/pom.xml | 2 +- 7 files changed, 8 insertions(+), 9 deletions(-) diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml index bbabeaf001..da15217a8b 100644 --- a/openid-connect-client/pom.xml +++ b/openid-connect-client/pom.xml @@ -21,7 +21,7 @@ openid-connect-parent org.mitre - 1.3.0-SNAPSHOT + 1.3.0-RC1 .. openid-connect-client diff --git a/openid-connect-common/pom.xml b/openid-connect-common/pom.xml index bbfbce2c8f..6d8bf57584 100644 --- a/openid-connect-common/pom.xml +++ b/openid-connect-common/pom.xml @@ -21,7 +21,7 @@ openid-connect-parent org.mitre - 1.3.0-SNAPSHOT + 1.3.0-RC1 .. openid-connect-common diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index 77a95afb67..0caa4d14da 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.3.0-SNAPSHOT + 1.3.0-RC1 openid-connect-server-webapp war diff --git a/openid-connect-server/pom.xml b/openid-connect-server/pom.xml index 0a893aa6e6..d7eb5486c0 100644 --- a/openid-connect-server/pom.xml +++ b/openid-connect-server/pom.xml @@ -22,7 +22,7 @@ org.mitre openid-connect-parent - 1.3.0-SNAPSHOT + 1.3.0-RC1 .. diff --git a/pom.xml b/pom.xml index 5d1880767b..9317b7aa4e 100644 --- a/pom.xml +++ b/pom.xml @@ -15,12 +15,11 @@ See the License for the specific language governing permissions and limitations under the License. --> - + 4.0.0 org.mitre openid-connect-parent - 1.3.0-SNAPSHOT + 1.3.0-RC1 MITREid Connect pom diff --git a/uma-server-webapp/pom.xml b/uma-server-webapp/pom.xml index 92593d88f0..d1cd2d8d89 100644 --- a/uma-server-webapp/pom.xml +++ b/uma-server-webapp/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.3.0-SNAPSHOT + 1.3.0-RC1 .. uma-server-webapp diff --git a/uma-server/pom.xml b/uma-server/pom.xml index 7038a9f752..0ceed2e3aa 100644 --- a/uma-server/pom.xml +++ b/uma-server/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.3.0-SNAPSHOT + 1.3.0-RC1 .. uma-server From 3e5e7a0f0b9fc9b20db7d5e9827c589f1c10d477 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 3 Mar 2017 18:03:26 -0500 Subject: [PATCH 139/269] [maven-release-plugin] prepare for next development iteration --- openid-connect-client/pom.xml | 2 +- openid-connect-common/pom.xml | 2 +- openid-connect-server-webapp/pom.xml | 2 +- openid-connect-server/pom.xml | 2 +- pom.xml | 2 +- uma-server-webapp/pom.xml | 2 +- uma-server/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml index da15217a8b..a42644801c 100644 --- a/openid-connect-client/pom.xml +++ b/openid-connect-client/pom.xml @@ -21,7 +21,7 @@ openid-connect-parent org.mitre - 1.3.0-RC1 + 1.3.0-RC2-SNAPSHOT .. openid-connect-client diff --git a/openid-connect-common/pom.xml b/openid-connect-common/pom.xml index 6d8bf57584..50cb25e7f7 100644 --- a/openid-connect-common/pom.xml +++ b/openid-connect-common/pom.xml @@ -21,7 +21,7 @@ openid-connect-parent org.mitre - 1.3.0-RC1 + 1.3.0-RC2-SNAPSHOT .. openid-connect-common diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index 0caa4d14da..141cf54bf8 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.3.0-RC1 + 1.3.0-RC2-SNAPSHOT openid-connect-server-webapp war diff --git a/openid-connect-server/pom.xml b/openid-connect-server/pom.xml index d7eb5486c0..c7e47dfdf7 100644 --- a/openid-connect-server/pom.xml +++ b/openid-connect-server/pom.xml @@ -22,7 +22,7 @@ org.mitre openid-connect-parent - 1.3.0-RC1 + 1.3.0-RC2-SNAPSHOT .. diff --git a/pom.xml b/pom.xml index 9317b7aa4e..9b9d7e443e 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 4.0.0 org.mitre openid-connect-parent - 1.3.0-RC1 + 1.3.0-RC2-SNAPSHOT MITREid Connect pom diff --git a/uma-server-webapp/pom.xml b/uma-server-webapp/pom.xml index d1cd2d8d89..00cec856eb 100644 --- a/uma-server-webapp/pom.xml +++ b/uma-server-webapp/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.3.0-RC1 + 1.3.0-RC2-SNAPSHOT .. uma-server-webapp diff --git a/uma-server/pom.xml b/uma-server/pom.xml index 0ceed2e3aa..6ac6925e81 100644 --- a/uma-server/pom.xml +++ b/uma-server/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.3.0-RC1 + 1.3.0-RC2-SNAPSHOT .. uma-server From 837cce786a9bbe4c437a38e186aad126d0eee7fb Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sat, 11 Mar 2017 15:29:53 -0500 Subject: [PATCH 140/269] moved special UI variable loading to inside dynamic footer block --- .../src/main/webapp/WEB-INF/tags/footer.tag | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag index a7de8fb97b..2b95de6dcb 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/footer.tag @@ -15,17 +15,6 @@ - @@ -35,6 +24,17 @@ + From 72fd3c2b99e6fb21000ef752078a2d89b2d1a734 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sat, 11 Mar 2017 15:27:39 -0500 Subject: [PATCH 141/269] added ID Token Validity Seconds to data import/export API --- .../openid/connect/service/impl/MITREidDataService_1_3.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java index c5d8750a06..6ce0f94811 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java @@ -116,6 +116,7 @@ public class MITREidDataService_1_3 extends MITREidDataServiceSupport implements private static final String REDIRECT_URIS = "redirectUris"; private static final String REFRESH_TOKEN_VALIDITY_SECONDS = "refreshTokenValiditySeconds"; private static final String ACCESS_TOKEN_VALIDITY_SECONDS = "accessTokenValiditySeconds"; + private static final String ID_TOKEN_VALIDITY_SECONDS = "idTokenValiditySeconds"; private static final String SECRET = "secret"; private static final String URI = "uri"; private static final String CREATOR_USER_ID = "creatorUserId"; @@ -450,6 +451,7 @@ private void writeClients(JsonWriter writer) { writer.endArray(); writer.name(ACCESS_TOKEN_VALIDITY_SECONDS).value(client.getAccessTokenValiditySeconds()); writer.name(REFRESH_TOKEN_VALIDITY_SECONDS).value(client.getRefreshTokenValiditySeconds()); + writer.name(ID_TOKEN_VALIDITY_SECONDS).value(client.getIdTokenValiditySeconds()); writer.name(REDIRECT_URIS); writeNullSafeArray(writer, client.getRedirectUris()); writer.name(CLAIMS_REDIRECT_URIS); @@ -1052,6 +1054,8 @@ private void readClients(JsonReader reader) throws IOException { client.setAccessTokenValiditySeconds(reader.nextInt()); } else if (name.equals(REFRESH_TOKEN_VALIDITY_SECONDS)) { client.setRefreshTokenValiditySeconds(reader.nextInt()); + } else if (name.equals(ID_TOKEN_VALIDITY_SECONDS)) { + client.setIdTokenValiditySeconds(reader.nextInt()); } else if (name.equals(REDIRECT_URIS)) { Set redirectUris = readSet(reader); client.setRedirectUris(redirectUris); From c42fe5736798bc14203ee675b888e2d38ab7559c Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 14 Mar 2017 17:39:44 -0400 Subject: [PATCH 142/269] changed task operations to print out name of operation on run --- .../data/AbstractPageOperationTemplate.java | 53 +++++++++++++++---- .../AbstractPageOperationTemplateTest.java | 2 +- ...DefaultOAuth2AuthorizationCodeService.java | 2 +- .../DefaultOAuth2ProviderTokenService.java | 6 +-- 4 files changed, 47 insertions(+), 16 deletions(-) diff --git a/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java b/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java index 3a8962aacf..3494307df3 100644 --- a/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java +++ b/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java @@ -40,6 +40,11 @@ public abstract class AbstractPageOperationTemplate { * swallowed during execution default true. */ private boolean swallowExceptions = true; + + /** + * String that is used for logging in final tallies. + */ + private String operationName = ""; /** @@ -47,11 +52,10 @@ public abstract class AbstractPageOperationTemplate { * maxPages and maxTime to DEFAULT_MAX_PAGES and * DEFAULT_MAX_TIME_MILLIS respectively */ - public AbstractPageOperationTemplate(){ - this(DEFAULT_MAX_PAGES, DEFAULT_MAX_TIME_MILLIS); + public AbstractPageOperationTemplate(String operationName){ + this(DEFAULT_MAX_PAGES, DEFAULT_MAX_TIME_MILLIS, operationName); } - /** * Instantiates a new AbstractPageOperationTemplate with the * given maxPages and maxTime @@ -59,12 +63,12 @@ public AbstractPageOperationTemplate(){ * @param maxPages the maximum number of pages to fetch. * @param maxTime the maximum execution time. */ - public AbstractPageOperationTemplate(int maxPages, long maxTime){ + public AbstractPageOperationTemplate(int maxPages, long maxTime, String operationName){ this.maxPages = maxPages; this.maxTime = maxTime; + this.operationName = operationName; } - /** * Execute the operation on each member of a page of results * retrieved through the fetch method. the method will execute @@ -74,8 +78,7 @@ public AbstractPageOperationTemplate(int maxPages, long maxTime){ * swallowException (default true) field is set true. */ public void execute(){ - logger.info("Starting execution of paged operation. maximum time: " + maxTime - + " maximum pages: " + maxPages); + logger.debug("[" + getOperationName() + "] Starting execution of paged operation. maximum time: " + maxTime + ", maximum pages: " + maxPages); long startTime = System.currentTimeMillis(); long executionTime = 0; @@ -112,12 +115,11 @@ public void execute(){ executionTime = System.currentTimeMillis() - startTime; } - logger.info("Paged operation run completed " + operationsCompleted + " swallowed " + exceptionsSwallowedCount + " exceptions"); - for(String className: exceptionsSwallowedClasses) { - logger.warn("Paged operation swallowed at least one exception of type " + className); - } + finalReport(operationsCompleted, exceptionsSwallowedCount, exceptionsSwallowedClasses); } + + /** * method responsible for fetching * a page of items. @@ -133,6 +135,19 @@ public void execute(){ * @param item the item */ protected abstract void doOperation(T item); + + /** + * Method responsible for final report of progress. + * @return + */ + protected void finalReport(int operationsCompleted, int exceptionsSwallowedCount, Set exceptionsSwallowedClasses) { + if (operationsCompleted > 0 || exceptionsSwallowedCount > 0) { + logger.info("[" + getOperationName() + "] Paged operation run: completed " + operationsCompleted + "; swallowed " + exceptionsSwallowedCount + " exceptions"); + } + for(String className: exceptionsSwallowedClasses) { + logger.warn("[" + getOperationName() + "] Paged operation swallowed at least one exception of type " + className); + } + } public int getMaxPages() { return maxPages; @@ -157,4 +172,20 @@ public boolean isSwallowExceptions() { public void setSwallowExceptions(boolean swallowExceptions) { this.swallowExceptions = swallowExceptions; } + + + /** + * @return the operationName + */ + public String getOperationName() { + return operationName; + } + + + /** + * @param operationName the operationName to set + */ + public void setOperationName(String operationName) { + this.operationName = operationName; + } } diff --git a/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java b/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java index 6dcc8a2bfa..46d8bf57eb 100644 --- a/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java +++ b/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java @@ -138,7 +138,7 @@ private static class CountingPageOperation extends AbstractPageOperationTemplate private long timeToPreviousFetch; private CountingPageOperation(int maxPages, long maxTime) { - super(maxPages, maxTime); + super(maxPages, maxTime, "CountingPageOperation"); startTime = System.currentTimeMillis(); } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java index 9c633e9ff2..59806caa2f 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java @@ -117,7 +117,7 @@ public OAuth2Authentication consumeAuthorizationCode(String code) throws Invalid @Transactional(value="defaultTransactionManager") public void clearExpiredAuthorizationCodes() { - new AbstractPageOperationTemplate(){ + new AbstractPageOperationTemplate("clearExpiredAuthorizationCodes"){ @Override public Collection fetchPage() { return repository.getExpiredCodes(); diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java index b7681752d4..ad0a664919 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java @@ -492,7 +492,7 @@ public List getRefreshTokensForClient(ClientDetailsEnt public void clearExpiredTokens() { logger.debug("Cleaning out all expired tokens"); - new AbstractPageOperationTemplate() { + new AbstractPageOperationTemplate("clearExpiredAccessTokens") { @Override public Collection fetchPage() { return tokenRepository.getAllExpiredAccessTokens(new DefaultPageCriteria()); @@ -504,7 +504,7 @@ public void doOperation(OAuth2AccessTokenEntity item) { } }.execute(); - new AbstractPageOperationTemplate() { + new AbstractPageOperationTemplate("clearExpiredRefreshTokens") { @Override public Collection fetchPage() { return tokenRepository.getAllExpiredRefreshTokens(new DefaultPageCriteria()); @@ -516,7 +516,7 @@ public void doOperation(OAuth2RefreshTokenEntity item) { } }.execute(); - new AbstractPageOperationTemplate() { + new AbstractPageOperationTemplate("clearExpiredAuthenticationHolders") { @Override public Collection fetchPage() { return authenticationHolderRepository.getOrphanedAuthenticationHolders(new DefaultPageCriteria()); From 3326eee934ae54f526e73715ddcffd377a52e5a0 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 8 Mar 2017 17:36:00 -0500 Subject: [PATCH 143/269] shell for device flow --- .../org/mitre/oauth2/model/DeviceCode.java | 207 ++++++++++++++++ .../oauth2/service/DeviceCodeService.java | 52 ++++ .../oauth2/token/ChainedTokenGranter.java | 6 +- .../oauth2/token/DeviceTokenGranter.java | 85 +++++++ .../org/mitre/oauth2/web/DeviceEndpoint.java | 229 ++++++++++++++++++ 5 files changed, 576 insertions(+), 3 deletions(-) create mode 100644 openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java create mode 100644 openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java create mode 100644 openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java create mode 100644 openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java new file mode 100644 index 0000000000..9f7fa8eba7 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java @@ -0,0 +1,207 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.oauth2.model; + +import java.util.Map; +import java.util.Set; + +import javax.persistence.Basic; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MapKeyColumn; +import javax.persistence.Table; + +/** + * @author jricher + * + */ +@Entity +@Table(name = "device_code") +public class DeviceCode { + + private Long id; + private String deviceCode; + private String userCode; + private Set scope; + private String clientId; + private Map requestParameters; + private boolean approved; + private AuthenticationHolderEntity authenticationHolder; + + public DeviceCode() { + + } + + public DeviceCode(String deviceCode, String userCode, Set scope, String clientId, Map params) { + this.deviceCode = deviceCode; + this.userCode = userCode; + this.scope = scope; + this.clientId = clientId; + this.requestParameters = params; + this.setApproved(false); + } + + /** + * @return the id + */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + public Long getId() { + return id; + } + + /** + * @param id the id to set + */ + public void setId(Long id) { + this.id = id; + } + + /** + * @return the deviceCode + */ + @Basic + @Column(name = "device_code") + public String getDeviceCode() { + return deviceCode; + } + + /** + * @param deviceCode the deviceCode to set + */ + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + /** + * @return the userCode + */ + @Basic + @Column(name = "user_code") + public String getUserCode() { + return userCode; + } + + /** + * @param userCode the userCode to set + */ + public void setUserCode(String userCode) { + this.userCode = userCode; + } + + /** + * @return the scope + */ + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable( + name="device_code_scope", + joinColumns=@JoinColumn(name="owner_id") + ) + @Column(name="scope") + public Set getScope() { + return scope; + } + + /** + * @param scope the scope to set + */ + public void setScope(Set scope) { + this.scope = scope; + } + + /** + * @return the clientId + */ + @Basic + @Column(name = "client_id") + public String getClientId() { + return clientId; + } + + /** + * @param clientId the clientId to set + */ + public void setClientId(String clientId) { + this.clientId = clientId; + } + + /** + * @return the params + */ + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable( + name="device_code_request_parameter", + joinColumns=@JoinColumn(name="owner_id") + ) + @Column(name="val") + @MapKeyColumn(name="param") + public Map getRequestParameters() { + return requestParameters; + } + + /** + * @param params the params to set + */ + public void setRequestParameters(Map params) { + this.requestParameters = params; + } + + /** + * @return the approved + */ + @Basic + @Column(name = "approved") + public boolean isApproved() { + return approved; + } + + /** + * @param approved the approved to set + */ + public void setApproved(boolean approved) { + this.approved = approved; + } + + /** + * The authentication in place when this token was created. + * @return the authentication + */ + @ManyToOne + @JoinColumn(name = "auth_holder_id") + public AuthenticationHolderEntity getAuthenticationHolder() { + return authenticationHolder; + } + + /** + * @param authentication the authentication to set + */ + public void setAuthenticationHolder(AuthenticationHolderEntity authenticationHolder) { + this.authenticationHolder = authenticationHolder; + } + + +} diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java new file mode 100644 index 0000000000..ed354744fe --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.oauth2.service; + +import org.mitre.oauth2.model.DeviceCode; +import org.springframework.security.oauth2.provider.ClientDetails; + +/** + * @author jricher + * + */ +public interface DeviceCodeService { + + /** + * @param dc + */ + public DeviceCode save(DeviceCode dc); + + /** + * @param userCode + * @return + */ + public DeviceCode lookUpByUserCode(String userCode); + + /** + * @param dc + */ + public DeviceCode approveDeviceCode(DeviceCode dc); + + /** + * @param deviceCode + * @param client + * @return + */ + public DeviceCode consumeDeviceCode(String deviceCode, ClientDetails client); + +} diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/token/ChainedTokenGranter.java b/openid-connect-server/src/main/java/org/mitre/oauth2/token/ChainedTokenGranter.java index 5663957428..acb36a5d2c 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/token/ChainedTokenGranter.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/token/ChainedTokenGranter.java @@ -45,7 +45,7 @@ @Component("chainedTokenGranter") public class ChainedTokenGranter extends AbstractTokenGranter { - private static final String grantType = "urn:ietf:params:oauth:grant_type:redelegate"; + public static final String GRANT_TYPE = "urn:ietf:params:oauth:grant_type:redelegate"; // keep down-cast versions so we can get to the right queries private OAuth2TokenEntityService tokenServices; @@ -53,11 +53,11 @@ public class ChainedTokenGranter extends AbstractTokenGranter { /** * @param tokenServices * @param clientDetailsService - * @param grantType + * @param GRANT_TYPE */ @Autowired public ChainedTokenGranter(OAuth2TokenEntityService tokenServices, ClientDetailsEntityService clientDetailsService, OAuth2RequestFactory requestFactory) { - super(tokenServices, clientDetailsService, requestFactory, grantType); + super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); this.tokenServices = tokenServices; } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java b/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java new file mode 100644 index 0000000000..5fd7fbd34c --- /dev/null +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.oauth2.token; + +import org.mitre.oauth2.model.DeviceCode; +import org.mitre.oauth2.service.DeviceCodeService; +import org.mitre.oauth2.web.DeviceEndpoint; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; +import org.springframework.security.oauth2.provider.ClientDetails; +import org.springframework.security.oauth2.provider.ClientDetailsService; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2RequestFactory; +import org.springframework.security.oauth2.provider.TokenRequest; +import org.springframework.security.oauth2.provider.token.AbstractTokenGranter; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; + +/** + * Implements https://tools.ietf.org/html/draft-ietf-oauth-device-flow + * + * @see DeviceEndpoint + * + * @author jricher + * + */ +public class DeviceTokenGranter extends AbstractTokenGranter { + + public static final String GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code"; + + @Autowired + private DeviceCodeService deviceCodeService; + + /** + * @param tokenServices + * @param clientDetailsService + * @param requestFactory + * @param grantType + */ + protected DeviceTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) { + super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); + } + + /* (non-Javadoc) + * @see org.springframework.security.oauth2.provider.token.AbstractTokenGranter#getOAuth2Authentication(org.springframework.security.oauth2.provider.ClientDetails, org.springframework.security.oauth2.provider.TokenRequest) + */ + @Override + protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { + + String deviceCode = tokenRequest.getRequestParameters().get("device_code"); + + // look up the device code and consume it + DeviceCode dc = deviceCodeService.consumeDeviceCode(deviceCode, client); + + if (dc != null) { + // inherit the (approved) scopes from the original request + tokenRequest.setScope(dc.getScope()); + + OAuth2Authentication auth = new OAuth2Authentication(getRequestFactory().createOAuth2Request(client, tokenRequest), dc.getAuthenticationHolder().getUserAuth()); + + return auth; + } else { + throw new InvalidGrantException("Invalid device code: " + deviceCode); + } + + } + + + + +} diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java new file mode 100644 index 0000000000..18e83a167e --- /dev/null +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java @@ -0,0 +1,229 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.oauth2.web; + +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.mitre.oauth2.model.ClientDetailsEntity; +import org.mitre.oauth2.model.DeviceCode; +import org.mitre.oauth2.model.SystemScope; +import org.mitre.oauth2.service.ClientDetailsEntityService; +import org.mitre.oauth2.service.DeviceCodeService; +import org.mitre.oauth2.service.SystemScopeService; +import org.mitre.oauth2.token.DeviceTokenGranter; +import org.mitre.openid.connect.config.ConfigurationPropertiesBean; +import org.mitre.openid.connect.view.HttpCodeView; +import org.mitre.openid.connect.view.JsonEntityView; +import org.mitre.openid.connect.view.JsonErrorView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.oauth2.common.exceptions.InvalidClientException; +import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; +import org.springframework.security.oauth2.common.util.OAuth2Utils; +import org.springframework.security.oauth2.common.util.RandomValueStringGenerator; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; + +/** + * Implements https://tools.ietf.org/html/draft-ietf-oauth-device-flow + * + * @see DeviceTokenGranter + * + * @author jricher + * + */ +@Controller +public class DeviceEndpoint { + + public static final String URL = "/device"; + + public static final Logger logger = LoggerFactory.getLogger(DeviceEndpoint.class); + + @Autowired + private ClientDetailsEntityService clientService; + + @Autowired + private SystemScopeService scopeService; + + @Autowired + private ConfigurationPropertiesBean config; + + @Autowired + private DeviceCodeService deviceCodeService; + + private RandomValueStringGenerator randomGenerator = new RandomValueStringGenerator(); + + @RequestMapping(value = URL, method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public String requestDeviceCode(@RequestParam("client_id") String clientId, @RequestParam(name="scope", required=false) String scope, Map parameters, ModelMap model) { + + ClientDetailsEntity client; + try { + client = clientService.loadClientByClientId(clientId); + + // make sure this client can do the device flow + + Collection authorizedGrantTypes = client.getAuthorizedGrantTypes(); + if (authorizedGrantTypes != null && !authorizedGrantTypes.isEmpty() + && !authorizedGrantTypes.contains(DeviceTokenGranter.GRANT_TYPE)) { + throw new InvalidClientException("Unauthorized grant type: " + DeviceTokenGranter.GRANT_TYPE); + } + + } catch (OAuth2Exception e) { + logger.error("OAuth2Exception was thrown when attempting to load client", e); + model.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST); + return HttpCodeView.VIEWNAME; + } catch (IllegalArgumentException e) { + logger.error("IllegalArgumentException was thrown when attempting to load client", e); + model.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST); + return HttpCodeView.VIEWNAME; + } + + if (client == null) { + logger.error("could not find client " + clientId); + model.put(HttpCodeView.CODE, HttpStatus.NOT_FOUND); + return HttpCodeView.VIEWNAME; + } + + // make sure the client is allowed to ask for those scopes + Set requestedScopes = OAuth2Utils.parseParameterList(scope); + Set allowedScopes = client.getScope(); + + if (!scopeService.scopesMatch(allowedScopes, requestedScopes)) { + // client asked for scopes it can't have + logger.error("Client asked for " + requestedScopes + " but is allowed " + allowedScopes); + model.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST); + model.put(JsonErrorView.ERROR, "invalid_scope"); + return JsonErrorView.VIEWNAME; + } + + // if we got here the request is legit + + // create a device code, should be big and random + String deviceCode = UUID.randomUUID().toString(); + + // create a user code, should be random but small and typable + String userCode = randomGenerator.generate(); + + // TODO: expiration + model.put(JsonEntityView.ENTITY, ImmutableMap.of( + "device_code", deviceCode, + "user_code", userCode, + "verification_uri", config.getIssuer() + URL + )); + + DeviceCode dc = new DeviceCode(deviceCode, userCode, requestedScopes, clientId, parameters); + + + deviceCodeService.save(dc); + + return JsonEntityView.VIEWNAME; + + } + + @PreAuthorize("hasRole('ROLE_USER')") + @RequestMapping(value = URL, method = RequestMethod.GET) + public String requestUserCode(ModelMap model) { + + // print out a page that asks the user to enter their user code + // user must be logged in + + return "requestUserCode"; + } + + @PreAuthorize("hasRole('ROLE_USER')") + @RequestMapping(value = URL + "/verify", method = RequestMethod.POST) + public String readUserCode(@RequestParam("userCode") String userCode, ModelMap model) { + + // look up the request based on the user code + DeviceCode dc = deviceCodeService.lookUpByUserCode(userCode); + + ClientDetailsEntity client = clientService.loadClientByClientId(dc.getClientId()); + + model.put("client", client); + model.put("dc", dc); + + // pre-process the scopes + Set scopes = scopeService.fromStrings(dc.getScope()); + + Set sortedScopes = new LinkedHashSet<>(scopes.size()); + Set systemScopes = scopeService.getAll(); + + // sort scopes for display based on the inherent order of system scopes + for (SystemScope s : systemScopes) { + if (scopes.contains(s)) { + sortedScopes.add(s); + } + } + + // add in any scopes that aren't system scopes to the end of the list + sortedScopes.addAll(Sets.difference(scopes, systemScopes)); + + model.put("scopes", sortedScopes); + + return "approveDevice"; + } + + @PreAuthorize("hasRole('ROLE_USER')") + @RequestMapping(value = URL + "/approve", method = RequestMethod.POST) + public String approveDevice(@RequestParam("userCode") String userCode, @RequestParam(value = "approve", required = false) String approve, ModelMap model) { + + + DeviceCode dc = deviceCodeService.lookUpByUserCode(userCode); + + DeviceCode approvedCode = deviceCodeService.approveDeviceCode(dc); + + ClientDetailsEntity client = clientService.loadClientByClientId(dc.getClientId()); + + model.put("client", client); + + // pre-process the scopes + Set scopes = scopeService.fromStrings(dc.getScope()); + + Set sortedScopes = new LinkedHashSet<>(scopes.size()); + Set systemScopes = scopeService.getAll(); + + // sort scopes for display based on the inherent order of system scopes + for (SystemScope s : systemScopes) { + if (scopes.contains(s)) { + sortedScopes.add(s); + } + } + + // add in any scopes that aren't system scopes to the end of the list + sortedScopes.addAll(Sets.difference(scopes, systemScopes)); + + model.put("scopes", sortedScopes); + + + return "deviceApproved"; + } +} From a5b41151691d136e5f12f11b7e7cd0679222a137 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 9 Mar 2017 20:08:47 -0500 Subject: [PATCH 144/269] functioning device code flow --- .../oauth2/service/DeviceCodeService.java | 5 +- .../src/main/resources/db/hsql/clients.sql | 1 + .../webapp/WEB-INF/application-context.xml | 14 + .../src/main/webapp/WEB-INF/authz-config.xml | 1 + .../webapp/WEB-INF/views/approveDevice.jsp | 286 ++++++++++++++++++ .../webapp/WEB-INF/views/deviceApproved.jsp | 33 ++ .../webapp/WEB-INF/views/requestUserCode.jsp | 48 +++ .../resources/js/locale/en/messages.json | 9 +- .../AuthorizationPendingException.java | 50 +++ .../oauth2/token/DeviceTokenGranter.java | 19 +- .../org/mitre/oauth2/web/DeviceEndpoint.java | 42 ++- 11 files changed, 493 insertions(+), 15 deletions(-) create mode 100644 openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approveDevice.jsp create mode 100644 openid-connect-server-webapp/src/main/webapp/WEB-INF/views/deviceApproved.jsp create mode 100644 openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp create mode 100644 openid-connect-server/src/main/java/org/mitre/oauth2/exception/AuthorizationPendingException.java diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java index ed354744fe..443bb674bf 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java @@ -19,6 +19,8 @@ import org.mitre.oauth2.model.DeviceCode; import org.springframework.security.oauth2.provider.ClientDetails; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; /** * @author jricher @@ -39,8 +41,9 @@ public interface DeviceCodeService { /** * @param dc + * @param o2Auth */ - public DeviceCode approveDeviceCode(DeviceCode dc); + public DeviceCode approveDeviceCode(DeviceCode dc, OAuth2Authentication o2Auth); /** * @param deviceCode diff --git a/openid-connect-server-webapp/src/main/resources/db/hsql/clients.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/clients.sql index 7e7a8a53d9..1410f7bd15 100644 --- a/openid-connect-server-webapp/src/main/resources/db/hsql/clients.sql +++ b/openid-connect-server-webapp/src/main/resources/db/hsql/clients.sql @@ -28,6 +28,7 @@ INSERT INTO client_redirect_uri_TEMP (owner_id, redirect_uri) VALUES INSERT INTO client_grant_type_TEMP (owner_id, grant_type) VALUES ('client', 'authorization_code'), ('client', 'urn:ietf:params:oauth:grant_type:redelegate'), + ('client', 'urn:ietf:params:oauth:grant-type:device_code'), ('client', 'implicit'), ('client', 'refresh_token'); diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml index bdbf0c52cc..49cb8d3e95 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml @@ -138,6 +138,20 @@ + + + + + + + + + + + diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approveDevice.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approveDevice.jsp new file mode 100644 index 0000000000..bcc6882ca2 --- /dev/null +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approveDevice.jsp @@ -0,0 +1,286 @@ +<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> +<%@ page import="org.springframework.security.core.AuthenticationException"%> +<%@ page import="org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException"%> +<%@ page import="org.springframework.security.web.WebAttributes"%> +<%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> +<%@ taglib prefix="o" tagdir="/WEB-INF/tags"%> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> + + + + +
    + <% if (session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) != null && !(session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) instanceof UnapprovedClientAuthenticationException)) { %> +
    + × + +

    + (<%= ((AuthenticationException) session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).getMessage() %>) +

    +
    + <% } %> + + +
    +

      + + + + + + + + +

    + + + +
    +
    + + + + +
    +

    + + + + +

    +
    +
    + + +
    "> +

    + : +

    + +

    + +

    +

    + + + + + + + + + + + +

    +
    +
    +
    +
    + + +
      +
    • + +
    • +
    + + +
    + +
    + + +
    + +
    +
    +
      + +
    • : ">
    • +
      + +
    • : ">
    • +
      + +
    • : ">
    • +
      + +
    • :
    • +
      +
    +
    +
    +
    +
    + + +
    + +
    +
    + +
    +
    +
    + : + + +
    +

    + : +

    +

    + +

    +
    +
    + + + + + + + + + + + + + + + + + +
  • + : + +
  • +
    + +
    + " + > + + + + + + + + + +
    + +
    + +
    +

    + + " + + + + + + + "? +

    + + + + + + +   + +
    + + + +
    +
  • + + diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/deviceApproved.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/deviceApproved.jsp new file mode 100644 index 0000000000..a31898fe93 --- /dev/null +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/deviceApproved.jsp @@ -0,0 +1,33 @@ +<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> +<%@ page import="org.springframework.security.core.AuthenticationException"%> +<%@ page import="org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException"%> +<%@ page import="org.springframework.security.web.WebAttributes"%> +<%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> +<%@ taglib prefix="o" tagdir="/WEB-INF/tags"%> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> + + + + +
    + +
    +

    + + + + + + + + +

    + +
    + +
    +
    + diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp new file mode 100644 index 0000000000..ec80d9b5dd --- /dev/null +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp @@ -0,0 +1,48 @@ +<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> +<%@ page import="org.springframework.security.core.AuthenticationException"%> +<%@ page import="org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException"%> +<%@ page import="org.springframework.security.web.WebAttributes"%> +<%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> +<%@ taglib prefix="o" tagdir="/WEB-INF/tags"%> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> + + + + +
    + +
    +

      + + + + + + + + + +

    + +
    + +
    +
    + + + + + +   + +
    +
    + +
    + +
    +
    + diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json index 1a0c7aed64..99329298ce 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json @@ -487,6 +487,13 @@ "password": "Password", "login-button": "Login", "error": "The system was unable to log you in. Please try again." + }, + "device": { + "request_code": { + "title": "Enter Code", + "header": "Enter code for ", + "description": "Enter the code displayed on your device into the box below and press submit", + "submit": "Submit" + } } - } \ No newline at end of file diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/exception/AuthorizationPendingException.java b/openid-connect-server/src/main/java/org/mitre/oauth2/exception/AuthorizationPendingException.java new file mode 100644 index 0000000000..e2ea2f71b1 --- /dev/null +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/exception/AuthorizationPendingException.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.oauth2.exception; + +import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; + +/** + * @author jricher + * + */ +public class AuthorizationPendingException extends OAuth2Exception { + + /** + * @param msg + */ + public AuthorizationPendingException(String msg) { + super(msg); + } + + /** + * + */ + private static final long serialVersionUID = -7078098692596870940L; + + /* (non-Javadoc) + * @see org.springframework.security.oauth2.common.exceptions.OAuth2Exception#getOAuth2ErrorCode() + */ + @Override + public String getOAuth2ErrorCode() { + return "authorization_pending"; + } + + + +} diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java b/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java index 5fd7fbd34c..f8252cab7a 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java @@ -17,6 +17,7 @@ package org.mitre.oauth2.token; +import org.mitre.oauth2.exception.AuthorizationPendingException; import org.mitre.oauth2.model.DeviceCode; import org.mitre.oauth2.service.DeviceCodeService; import org.mitre.oauth2.web.DeviceEndpoint; @@ -29,6 +30,7 @@ import org.springframework.security.oauth2.provider.TokenRequest; import org.springframework.security.oauth2.provider.token.AbstractTokenGranter; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; +import org.springframework.stereotype.Component; /** * Implements https://tools.ietf.org/html/draft-ietf-oauth-device-flow @@ -38,6 +40,7 @@ * @author jricher * */ +@Component("deviceTokenGranter") public class DeviceTokenGranter extends AbstractTokenGranter { public static final String GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code"; @@ -67,12 +70,20 @@ protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, Tok DeviceCode dc = deviceCodeService.consumeDeviceCode(deviceCode, client); if (dc != null) { - // inherit the (approved) scopes from the original request - tokenRequest.setScope(dc.getScope()); - OAuth2Authentication auth = new OAuth2Authentication(getRequestFactory().createOAuth2Request(client, tokenRequest), dc.getAuthenticationHolder().getUserAuth()); + if (dc.isApproved()) { - return auth; + // inherit the (approved) scopes from the original request + tokenRequest.setScope(dc.getScope()); + + OAuth2Authentication auth = new OAuth2Authentication(getRequestFactory().createOAuth2Request(client, tokenRequest), dc.getAuthenticationHolder().getUserAuth()); + + return auth; + } else { + + // still waiting for approval + throw new AuthorizationPendingException("Authorization pending for code " + deviceCode); + } } else { throw new InvalidGrantException("Invalid device code: " + deviceCode); } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java index 18e83a167e..975f0b0737 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java @@ -23,6 +23,8 @@ import java.util.Set; import java.util.UUID; +import javax.servlet.http.HttpSession; + import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.DeviceCode; import org.mitre.oauth2.model.SystemScope; @@ -40,10 +42,15 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.common.exceptions.InvalidClientException; import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; import org.springframework.security.oauth2.common.util.OAuth2Utils; import org.springframework.security.oauth2.common.util.RandomValueStringGenerator; +import org.springframework.security.oauth2.provider.AuthorizationRequest; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; +import org.springframework.security.oauth2.provider.OAuth2RequestFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; @@ -64,7 +71,8 @@ @Controller public class DeviceEndpoint { - public static final String URL = "/device"; + public static final String URL = "device"; + public static final String USER_URL = "device-user"; public static final Logger logger = LoggerFactory.getLogger(DeviceEndpoint.class); @@ -80,9 +88,12 @@ public class DeviceEndpoint { @Autowired private DeviceCodeService deviceCodeService; + @Autowired + private OAuth2RequestFactory oAuth2RequestFactory; + private RandomValueStringGenerator randomGenerator = new RandomValueStringGenerator(); - @RequestMapping(value = URL, method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @RequestMapping(value = "/" + URL, method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public String requestDeviceCode(@RequestParam("client_id") String clientId, @RequestParam(name="scope", required=false) String scope, Map parameters, ModelMap model) { ClientDetailsEntity client; @@ -150,7 +161,7 @@ public String requestDeviceCode(@RequestParam("client_id") String clientId, @Req } @PreAuthorize("hasRole('ROLE_USER')") - @RequestMapping(value = URL, method = RequestMethod.GET) + @RequestMapping(value = "/" + USER_URL, method = RequestMethod.GET) public String requestUserCode(ModelMap model) { // print out a page that asks the user to enter their user code @@ -160,8 +171,8 @@ public String requestUserCode(ModelMap model) { } @PreAuthorize("hasRole('ROLE_USER')") - @RequestMapping(value = URL + "/verify", method = RequestMethod.POST) - public String readUserCode(@RequestParam("userCode") String userCode, ModelMap model) { + @RequestMapping(value = "/" + USER_URL + "/verify", method = RequestMethod.POST) + public String readUserCode(@RequestParam("user_code") String userCode, ModelMap model, HttpSession session) { // look up the request based on the user code DeviceCode dc = deviceCodeService.lookUpByUserCode(userCode); @@ -189,17 +200,30 @@ public String readUserCode(@RequestParam("userCode") String userCode, ModelMap m model.put("scopes", sortedScopes); + AuthorizationRequest authorizationRequest = oAuth2RequestFactory.createAuthorizationRequest(dc.getRequestParameters()); + + session.setAttribute("authorizationRequest", authorizationRequest); + session.setAttribute("deviceCode", dc); + return "approveDevice"; } @PreAuthorize("hasRole('ROLE_USER')") - @RequestMapping(value = URL + "/approve", method = RequestMethod.POST) - public String approveDevice(@RequestParam("userCode") String userCode, @RequestParam(value = "approve", required = false) String approve, ModelMap model) { + @RequestMapping(value = "/" + USER_URL + "/approve", method = RequestMethod.POST) + public String approveDevice(@RequestParam("user_code") String userCode, @RequestParam(value = "user_oauth_approval") String approve, ModelMap model, Authentication auth, HttpSession session) { + AuthorizationRequest authorizationRequest = (AuthorizationRequest) session.getAttribute("authorizationRequest"); + DeviceCode dc = (DeviceCode) session.getAttribute("deviceCode"); - DeviceCode dc = deviceCodeService.lookUpByUserCode(userCode); + if (!dc.getUserCode().equals(userCode)) { + // TODO: return an error + return "error"; + } - DeviceCode approvedCode = deviceCodeService.approveDeviceCode(dc); + OAuth2Request o2req = oAuth2RequestFactory.createOAuth2Request(authorizationRequest); + OAuth2Authentication o2Auth = new OAuth2Authentication(o2req, auth); + + DeviceCode approvedCode = deviceCodeService.approveDeviceCode(dc, o2Auth); ClientDetailsEntity client = clientService.loadClientByClientId(dc.getClientId()); From 9cb5377ce82b4d012e95d4c2ae1cc1abf4da186b Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sat, 11 Mar 2017 15:28:29 -0500 Subject: [PATCH 145/269] added device code validity seconds to client model --- .../mitre/oauth2/model/ClientDetailsEntity.java | 17 +++++++++++++++++ .../mitre/oauth2/model/RegisteredClient.java | 16 ++++++++++++++++ .../resources/db/hsql/hsql_database_tables.sql | 1 + .../service/impl/MITREidDataService_1_3.java | 4 ++++ 4 files changed, 38 insertions(+) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java index 7eb0246972..ed9e511ba1 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java @@ -145,6 +145,7 @@ public class ClientDetailsEntity implements ClientDetails { private Integer idTokenValiditySeconds; //timeout for id tokens private Date createdAt; // time the client was created private boolean clearAccessTokensOnRefresh = true; // do we clear access tokens on refresh? + private Integer deviceCodeValiditySeconds; // timeout for device codes /** fields for UMA */ private Set claimsRedirectUris; @@ -1032,4 +1033,20 @@ public void setCodeChallengeMethod(PKCEAlgorithm codeChallengeMethod) { this.codeChallengeMethod = codeChallengeMethod; } + /** + * @return the deviceCodeValiditySeconds + */ + @Basic + @Column(name="device_code_validity_seconds") + public Integer getDeviceCodeValiditySeconds() { + return deviceCodeValiditySeconds; + } + + /** + * @param deviceCodeValiditySeconds the deviceCodeValiditySeconds to set + */ + public void setDeviceCodeValiditySeconds(Integer deviceCodeValiditySeconds) { + this.deviceCodeValiditySeconds = deviceCodeValiditySeconds; + } + } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java index a0e8a9312c..f5e195899d 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java @@ -846,6 +846,22 @@ public void setSource(JsonObject src) { this.src = src; } + /** + * @return + * @see org.mitre.oauth2.model.ClientDetailsEntity#getDeviceCodeValiditySeconds() + */ + public Integer getDeviceCodeValiditySeconds() { + return client.getDeviceCodeValiditySeconds(); + } + + /** + * @param deviceCodeValiditySeconds + * @see org.mitre.oauth2.model.ClientDetailsEntity#setDeviceCodeValiditySeconds(java.lang.Integer) + */ + public void setDeviceCodeValiditySeconds(Integer deviceCodeValiditySeconds) { + client.setDeviceCodeValiditySeconds(deviceCodeValiditySeconds); + } + } diff --git a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql index 23493f0eae..bf8e827f91 100644 --- a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql @@ -131,6 +131,7 @@ CREATE TABLE IF NOT EXISTS client_details ( dynamically_registered BOOLEAN DEFAULT false NOT NULL, allow_introspection BOOLEAN DEFAULT false NOT NULL, id_token_validity_seconds BIGINT DEFAULT 600 NOT NULL, + device_code_validity_seconds BIGINT DEFAULT 600 NOT NULL, client_id VARCHAR(256), client_secret VARCHAR(2048), diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java index 6ce0f94811..be2fb65f1e 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java @@ -117,6 +117,7 @@ public class MITREidDataService_1_3 extends MITREidDataServiceSupport implements private static final String REFRESH_TOKEN_VALIDITY_SECONDS = "refreshTokenValiditySeconds"; private static final String ACCESS_TOKEN_VALIDITY_SECONDS = "accessTokenValiditySeconds"; private static final String ID_TOKEN_VALIDITY_SECONDS = "idTokenValiditySeconds"; + private static final String DEVICE_CODE_VALIDITY_SECONDS = "deviceCodeValiditySeconds"; private static final String SECRET = "secret"; private static final String URI = "uri"; private static final String CREATOR_USER_ID = "creatorUserId"; @@ -452,6 +453,7 @@ private void writeClients(JsonWriter writer) { writer.name(ACCESS_TOKEN_VALIDITY_SECONDS).value(client.getAccessTokenValiditySeconds()); writer.name(REFRESH_TOKEN_VALIDITY_SECONDS).value(client.getRefreshTokenValiditySeconds()); writer.name(ID_TOKEN_VALIDITY_SECONDS).value(client.getIdTokenValiditySeconds()); + writer.name(DEVICE_CODE_VALIDITY_SECONDS).value(client.getDeviceCodeValiditySeconds()); writer.name(REDIRECT_URIS); writeNullSafeArray(writer, client.getRedirectUris()); writer.name(CLAIMS_REDIRECT_URIS); @@ -1056,6 +1058,8 @@ private void readClients(JsonReader reader) throws IOException { client.setRefreshTokenValiditySeconds(reader.nextInt()); } else if (name.equals(ID_TOKEN_VALIDITY_SECONDS)) { client.setIdTokenValiditySeconds(reader.nextInt()); + } else if (name.equals(DEVICE_CODE_VALIDITY_SECONDS)) { + client.setDeviceCodeValiditySeconds(reader.nextInt()); } else if (name.equals(REDIRECT_URIS)) { Set redirectUris = readSet(reader); client.setRedirectUris(redirectUris); From 548dad4e2958a4e56b29572b4c733abfdf17e780 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sat, 11 Mar 2017 15:29:08 -0500 Subject: [PATCH 146/269] added expiration to device codes --- .../org/mitre/oauth2/model/DeviceCode.java | 15 +- .../oauth2/service/DeviceCodeService.java | 19 +- .../bootstrap2/css/bootstrap-responsive.css | 1089 ++++ .../resources/bootstrap2/css/bootstrap.css | 5236 +++++++++++++++++ .../exception/DeviceCodeExpiredException.java | 48 + .../impl/InMemoryDeviceCodeService.java | 102 + .../oauth2/token/DeviceTokenGranter.java | 18 +- .../org/mitre/oauth2/web/DeviceEndpoint.java | 45 +- 8 files changed, 6554 insertions(+), 18 deletions(-) create mode 100644 openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.css create mode 100644 openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.css create mode 100644 openid-connect-server/src/main/java/org/mitre/oauth2/exception/DeviceCodeExpiredException.java create mode 100644 openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/InMemoryDeviceCodeService.java diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java index 9f7fa8eba7..8f7486b9fe 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java @@ -17,6 +17,7 @@ package org.mitre.oauth2.model; +import java.util.Date; import java.util.Map; import java.util.Set; @@ -33,6 +34,7 @@ import javax.persistence.ManyToOne; import javax.persistence.MapKeyColumn; import javax.persistence.Table; +import javax.persistence.Temporal; /** * @author jricher @@ -46,6 +48,7 @@ public class DeviceCode { private String deviceCode; private String userCode; private Set scope; + private Date expiration; private String clientId; private Map requestParameters; private boolean approved; @@ -61,7 +64,6 @@ public DeviceCode(String deviceCode, String userCode, Set scope, String this.scope = scope; this.clientId = clientId; this.requestParameters = params; - this.setApproved(false); } /** @@ -133,6 +135,17 @@ public void setScope(Set scope) { this.scope = scope; } + @Basic + @Temporal(javax.persistence.TemporalType.TIMESTAMP) + @Column(name = "expiration") + public Date getExpiration() { + return expiration; + } + + public void setExpiration(Date expiration) { + this.expiration = expiration; + } + /** * @return the clientId */ diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java index 443bb674bf..6d5810e204 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java @@ -17,6 +17,10 @@ package org.mitre.oauth2.service; +import java.util.Map; +import java.util.Set; + +import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.DeviceCode; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.OAuth2Authentication; @@ -28,11 +32,6 @@ */ public interface DeviceCodeService { - /** - * @param dc - */ - public DeviceCode save(DeviceCode dc); - /** * @param userCode * @return @@ -52,4 +51,14 @@ public interface DeviceCodeService { */ public DeviceCode consumeDeviceCode(String deviceCode, ClientDetails client); + /** + * @param deviceCode + * @param userCode + * @param requestedScopes + * @param client + * @param parameters + * @return + */ + public DeviceCode createNewDeviceCode(String deviceCode, String userCode, Set requestedScopes, ClientDetailsEntity client, Map parameters); + } diff --git a/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.css b/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.css new file mode 100644 index 0000000000..ac63813ee1 --- /dev/null +++ b/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.css @@ -0,0 +1,1089 @@ +/*! + * Bootstrap Responsive v2.3.2 + * + * Copyright 2013 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world by @mdo and @fat. + */ +.clearfix { + *zoom: 1; +} +.clearfix:before, +.clearfix:after { + display: table; + content: ""; + line-height: 0; +} +.clearfix:after { + clear: both; +} +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +@-ms-viewport { + width: device-width; +} +.hidden { + display: none; + visibility: hidden; +} +.visible-phone { + display: none !important; +} +.visible-tablet { + display: none !important; +} +.hidden-desktop { + display: none !important; +} +.visible-desktop { + display: inherit !important; +} +@media (min-width: 768px) and (max-width: 979px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important ; + } + .visible-tablet { + display: inherit !important; + } + .hidden-tablet { + display: none !important; + } +} +@media (max-width: 767px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important; + } + .visible-phone { + display: inherit !important; + } + .hidden-phone { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: inherit !important; + } + .hidden-print { + display: none !important; + } +} +@media (min-width: 1200px) { + .row { + margin-left: -30px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + content: ""; + line-height: 0; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 30px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 1170px; + } + .span12 { + width: 1170px; + } + .span11 { + width: 1070px; + } + .span10 { + width: 970px; + } + .span9 { + width: 870px; + } + .span8 { + width: 770px; + } + .span7 { + width: 670px; + } + .span6 { + width: 570px; + } + .span5 { + width: 470px; + } + .span4 { + width: 370px; + } + .span3 { + width: 270px; + } + .span2 { + width: 170px; + } + .span1 { + width: 70px; + } + .offset12 { + margin-left: 1230px; + } + .offset11 { + margin-left: 1130px; + } + .offset10 { + margin-left: 1030px; + } + .offset9 { + margin-left: 930px; + } + .offset8 { + margin-left: 830px; + } + .offset7 { + margin-left: 730px; + } + .offset6 { + margin-left: 630px; + } + .offset5 { + margin-left: 530px; + } + .offset4 { + margin-left: 430px; + } + .offset3 { + margin-left: 330px; + } + .offset2 { + margin-left: 230px; + } + .offset1 { + margin-left: 130px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + content: ""; + line-height: 0; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + float: left; + margin-left: 2.56410256%; + *margin-left: 2.51091107%; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.56410256%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851%; + } + .row-fluid .span11 { + width: 91.45299145%; + *width: 91.39979996%; + } + .row-fluid .span10 { + width: 82.90598291%; + *width: 82.85279142%; + } + .row-fluid .span9 { + width: 74.35897436%; + *width: 74.30578287%; + } + .row-fluid .span8 { + width: 65.81196581%; + *width: 65.75877432%; + } + .row-fluid .span7 { + width: 57.26495726%; + *width: 57.21176578%; + } + .row-fluid .span6 { + width: 48.71794872%; + *width: 48.66475723%; + } + .row-fluid .span5 { + width: 40.17094017%; + *width: 40.11774868%; + } + .row-fluid .span4 { + width: 31.62393162%; + *width: 31.57074013%; + } + .row-fluid .span3 { + width: 23.07692308%; + *width: 23.02373159%; + } + .row-fluid .span2 { + width: 14.52991453%; + *width: 14.47672304%; + } + .row-fluid .span1 { + width: 5.98290598%; + *width: 5.92971449%; + } + .row-fluid .offset12 { + margin-left: 105.12820513%; + *margin-left: 105.02182215%; + } + .row-fluid .offset12:first-child { + margin-left: 102.56410256%; + *margin-left: 102.45771959%; + } + .row-fluid .offset11 { + margin-left: 96.58119658%; + *margin-left: 96.4748136%; + } + .row-fluid .offset11:first-child { + margin-left: 94.01709402%; + *margin-left: 93.91071104%; + } + .row-fluid .offset10 { + margin-left: 88.03418803%; + *margin-left: 87.92780506%; + } + .row-fluid .offset10:first-child { + margin-left: 85.47008547%; + *margin-left: 85.36370249%; + } + .row-fluid .offset9 { + margin-left: 79.48717949%; + *margin-left: 79.38079651%; + } + .row-fluid .offset9:first-child { + margin-left: 76.92307692%; + *margin-left: 76.81669394%; + } + .row-fluid .offset8 { + margin-left: 70.94017094%; + *margin-left: 70.83378796%; + } + .row-fluid .offset8:first-child { + margin-left: 68.37606838%; + *margin-left: 68.2696854%; + } + .row-fluid .offset7 { + margin-left: 62.39316239%; + *margin-left: 62.28677941%; + } + .row-fluid .offset7:first-child { + margin-left: 59.82905983%; + *margin-left: 59.72267685%; + } + .row-fluid .offset6 { + margin-left: 53.84615385%; + *margin-left: 53.73977087%; + } + .row-fluid .offset6:first-child { + margin-left: 51.28205128%; + *margin-left: 51.1756683%; + } + .row-fluid .offset5 { + margin-left: 45.2991453%; + *margin-left: 45.19276232%; + } + .row-fluid .offset5:first-child { + margin-left: 42.73504274%; + *margin-left: 42.62865976%; + } + .row-fluid .offset4 { + margin-left: 36.75213675%; + *margin-left: 36.64575377%; + } + .row-fluid .offset4:first-child { + margin-left: 34.18803419%; + *margin-left: 34.08165121%; + } + .row-fluid .offset3 { + margin-left: 28.20512821%; + *margin-left: 28.09874523%; + } + .row-fluid .offset3:first-child { + margin-left: 25.64102564%; + *margin-left: 25.53464266%; + } + .row-fluid .offset2 { + margin-left: 19.65811966%; + *margin-left: 19.55173668%; + } + .row-fluid .offset2:first-child { + margin-left: 17.09401709%; + *margin-left: 16.98763412%; + } + .row-fluid .offset1 { + margin-left: 11.11111111%; + *margin-left: 11.00472813%; + } + .row-fluid .offset1:first-child { + margin-left: 8.54700855%; + *margin-left: 8.44062557%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 30px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 1156px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 1056px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 956px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 856px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 756px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 656px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 556px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 456px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 356px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 256px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 156px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 56px; + } + .thumbnails { + margin-left: -30px; + } + .thumbnails > li { + margin-left: 30px; + } + .row-fluid .thumbnails { + margin-left: 0; + } +} +@media (min-width: 768px) and (max-width: 979px) { + .row { + margin-left: -20px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + content: ""; + line-height: 0; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 724px; + } + .span12 { + width: 724px; + } + .span11 { + width: 662px; + } + .span10 { + width: 600px; + } + .span9 { + width: 538px; + } + .span8 { + width: 476px; + } + .span7 { + width: 414px; + } + .span6 { + width: 352px; + } + .span5 { + width: 290px; + } + .span4 { + width: 228px; + } + .span3 { + width: 166px; + } + .span2 { + width: 104px; + } + .span1 { + width: 42px; + } + .offset12 { + margin-left: 764px; + } + .offset11 { + margin-left: 702px; + } + .offset10 { + margin-left: 640px; + } + .offset9 { + margin-left: 578px; + } + .offset8 { + margin-left: 516px; + } + .offset7 { + margin-left: 454px; + } + .offset6 { + margin-left: 392px; + } + .offset5 { + margin-left: 330px; + } + .offset4 { + margin-left: 268px; + } + .offset3 { + margin-left: 206px; + } + .offset2 { + margin-left: 144px; + } + .offset1 { + margin-left: 82px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + content: ""; + line-height: 0; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + float: left; + margin-left: 2.76243094%; + *margin-left: 2.70923945%; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.76243094%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851%; + } + .row-fluid .span11 { + width: 91.43646409%; + *width: 91.3832726%; + } + .row-fluid .span10 { + width: 82.87292818%; + *width: 82.81973669%; + } + .row-fluid .span9 { + width: 74.30939227%; + *width: 74.25620078%; + } + .row-fluid .span8 { + width: 65.74585635%; + *width: 65.69266486%; + } + .row-fluid .span7 { + width: 57.18232044%; + *width: 57.12912895%; + } + .row-fluid .span6 { + width: 48.61878453%; + *width: 48.56559304%; + } + .row-fluid .span5 { + width: 40.05524862%; + *width: 40.00205713%; + } + .row-fluid .span4 { + width: 31.49171271%; + *width: 31.43852122%; + } + .row-fluid .span3 { + width: 22.9281768%; + *width: 22.87498531%; + } + .row-fluid .span2 { + width: 14.36464088%; + *width: 14.31144939%; + } + .row-fluid .span1 { + width: 5.80110497%; + *width: 5.74791348%; + } + .row-fluid .offset12 { + margin-left: 105.52486188%; + *margin-left: 105.4184789%; + } + .row-fluid .offset12:first-child { + margin-left: 102.76243094%; + *margin-left: 102.65604796%; + } + .row-fluid .offset11 { + margin-left: 96.96132597%; + *margin-left: 96.85494299%; + } + .row-fluid .offset11:first-child { + margin-left: 94.19889503%; + *margin-left: 94.09251205%; + } + .row-fluid .offset10 { + margin-left: 88.39779006%; + *margin-left: 88.29140708%; + } + .row-fluid .offset10:first-child { + margin-left: 85.63535912%; + *margin-left: 85.52897614%; + } + .row-fluid .offset9 { + margin-left: 79.83425414%; + *margin-left: 79.72787116%; + } + .row-fluid .offset9:first-child { + margin-left: 77.0718232%; + *margin-left: 76.96544023%; + } + .row-fluid .offset8 { + margin-left: 71.27071823%; + *margin-left: 71.16433525%; + } + .row-fluid .offset8:first-child { + margin-left: 68.50828729%; + *margin-left: 68.40190431%; + } + .row-fluid .offset7 { + margin-left: 62.70718232%; + *margin-left: 62.60079934%; + } + .row-fluid .offset7:first-child { + margin-left: 59.94475138%; + *margin-left: 59.8383684%; + } + .row-fluid .offset6 { + margin-left: 54.14364641%; + *margin-left: 54.03726343%; + } + .row-fluid .offset6:first-child { + margin-left: 51.38121547%; + *margin-left: 51.27483249%; + } + .row-fluid .offset5 { + margin-left: 45.5801105%; + *margin-left: 45.47372752%; + } + .row-fluid .offset5:first-child { + margin-left: 42.81767956%; + *margin-left: 42.71129658%; + } + .row-fluid .offset4 { + margin-left: 37.01657459%; + *margin-left: 36.91019161%; + } + .row-fluid .offset4:first-child { + margin-left: 34.25414365%; + *margin-left: 34.14776067%; + } + .row-fluid .offset3 { + margin-left: 28.45303867%; + *margin-left: 28.3466557%; + } + .row-fluid .offset3:first-child { + margin-left: 25.69060773%; + *margin-left: 25.58422476%; + } + .row-fluid .offset2 { + margin-left: 19.88950276%; + *margin-left: 19.78311978%; + } + .row-fluid .offset2:first-child { + margin-left: 17.12707182%; + *margin-left: 17.02068884%; + } + .row-fluid .offset1 { + margin-left: 11.32596685%; + *margin-left: 11.21958387%; + } + .row-fluid .offset1:first-child { + margin-left: 8.56353591%; + *margin-left: 8.45715293%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 710px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 648px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 586px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 524px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 462px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 400px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 338px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 276px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 214px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 152px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 90px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 28px; + } +} +@media (max-width: 767px) { + body { + padding-left: 20px; + padding-right: 20px; + } + .navbar-fixed-top, + .navbar-fixed-bottom, + .navbar-static-top { + margin-left: -20px; + margin-right: -20px; + } + .container-fluid { + padding: 0; + } + .dl-horizontal dt { + float: none; + clear: none; + width: auto; + text-align: left; + } + .dl-horizontal dd { + margin-left: 0; + } + .container { + width: auto; + } + .row-fluid { + width: 100%; + } + .row, + .thumbnails { + margin-left: 0; + } + .thumbnails > li { + float: none; + margin-left: 0; + } + [class*="span"], + .uneditable-input[class*="span"], + .row-fluid [class*="span"] { + float: none; + display: block; + width: 100%; + margin-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .span12, + .row-fluid .span12 { + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="offset"]:first-child { + margin-left: 0; + } + .input-large, + .input-xlarge, + .input-xxlarge, + input[class*="span"], + select[class*="span"], + textarea[class*="span"], + .uneditable-input { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .input-prepend input, + .input-append input, + .input-prepend input[class*="span"], + .input-append input[class*="span"] { + display: inline-block; + width: auto; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 0; + } + .modal { + position: fixed; + top: 20px; + left: 20px; + right: 20px; + width: auto; + margin: 0; + } + .modal.fade { + top: -100px; + } + .modal.fade.in { + top: 20px; + } +} +@media (max-width: 480px) { + .nav-collapse { + -webkit-transform: translate3d(0, 0, 0); + } + .page-header h1 small { + display: block; + line-height: 20px; + } + input[type="checkbox"], + input[type="radio"] { + border: 1px solid #ccc; + } + .form-horizontal .control-label { + float: none; + width: auto; + padding-top: 0; + text-align: left; + } + .form-horizontal .controls { + margin-left: 0; + } + .form-horizontal .control-list { + padding-top: 0; + } + .form-horizontal .form-actions { + padding-left: 10px; + padding-right: 10px; + } + .media .pull-left, + .media .pull-right { + float: none; + display: block; + margin-bottom: 10px; + } + .media-object { + margin-right: 0; + margin-left: 0; + } + .modal { + top: 10px; + left: 10px; + right: 10px; + } + .modal-header .close { + padding: 10px; + margin: -10px; + } + .carousel-caption { + position: static; + } +} +@media (max-width: 979px) { + body { + padding-top: 0; + } + .navbar-fixed-top, + .navbar-fixed-bottom { + position: static; + } + .navbar-fixed-top { + margin-bottom: 20px; + } + .navbar-fixed-bottom { + margin-top: 20px; + } + .navbar-fixed-top .navbar-inner, + .navbar-fixed-bottom .navbar-inner { + padding: 5px; + } + .navbar .container { + width: auto; + padding: 0; + } + .navbar .brand { + padding-left: 10px; + padding-right: 10px; + margin: 0 0 0 -5px; + } + .nav-collapse { + clear: both; + } + .nav-collapse .nav { + float: none; + margin: 0 0 10px; + } + .nav-collapse .nav > li { + float: none; + } + .nav-collapse .nav > li > a { + margin-bottom: 2px; + } + .nav-collapse .nav > .divider-vertical { + display: none; + } + .nav-collapse .nav .nav-header { + color: #777777; + text-shadow: none; + } + .nav-collapse .nav > li > a, + .nav-collapse .dropdown-menu a { + padding: 9px 15px; + font-weight: bold; + color: #777777; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + } + .nav-collapse .btn { + padding: 4px 10px 4px; + font-weight: normal; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + } + .nav-collapse .dropdown-menu li + li a { + margin-bottom: 2px; + } + .nav-collapse .nav > li > a:hover, + .nav-collapse .nav > li > a:focus, + .nav-collapse .dropdown-menu a:hover, + .nav-collapse .dropdown-menu a:focus { + background-color: #f2f2f2; + } + .navbar-inverse .nav-collapse .nav > li > a, + .navbar-inverse .nav-collapse .dropdown-menu a { + color: #999999; + } + .navbar-inverse .nav-collapse .nav > li > a:hover, + .navbar-inverse .nav-collapse .nav > li > a:focus, + .navbar-inverse .nav-collapse .dropdown-menu a:hover, + .navbar-inverse .nav-collapse .dropdown-menu a:focus { + background-color: #111111; + } + .nav-collapse.in .btn-group { + margin-top: 5px; + padding: 0; + } + .nav-collapse .dropdown-menu { + position: static; + top: auto; + left: auto; + float: none; + display: none; + max-width: none; + margin: 0 15px; + padding: 0; + background-color: transparent; + border: none; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + } + .nav-collapse .open > .dropdown-menu { + display: block; + } + .nav-collapse .dropdown-menu:before, + .nav-collapse .dropdown-menu:after { + display: none; + } + .nav-collapse .dropdown-menu .divider { + display: none; + } + .nav-collapse .nav > li > .dropdown-menu:before, + .nav-collapse .nav > li > .dropdown-menu:after { + display: none; + } + .nav-collapse .navbar-form, + .nav-collapse .navbar-search { + float: none; + padding: 10px 15px; + margin: 10px 0; + border-top: 1px solid #f2f2f2; + border-bottom: 1px solid #f2f2f2; + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1); + -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1); + box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1); + } + .navbar-inverse .nav-collapse .navbar-form, + .navbar-inverse .nav-collapse .navbar-search { + border-top-color: #111111; + border-bottom-color: #111111; + } + .navbar .nav-collapse .nav.pull-right { + float: none; + margin-left: 0; + } + .nav-collapse, + .nav-collapse.collapse { + overflow: hidden; + height: 0; + } + .navbar .btn-navbar { + display: block; + } + .navbar-static .navbar-inner { + padding-left: 10px; + padding-right: 10px; + } +} +@media (min-width: 979px + 1) { + .nav-collapse.collapse { + height: auto !important; + overflow: visible !important; + } +} + diff --git a/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.css b/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.css new file mode 100644 index 0000000000..dd320d684b --- /dev/null +++ b/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.css @@ -0,0 +1,5236 @@ +/*! + * Bootstrap v2.3.2 + * + * Copyright 2013 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world by @mdo and @fat. + */ +.clearfix { + *zoom: 1; +} +.clearfix:before, +.clearfix:after { + display: table; + content: ""; + line-height: 0; +} +.clearfix:after { + clear: both; +} +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} +audio:not([controls]) { + display: none; +} +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +a:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +a:hover, +a:active { + outline: 0; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} +img { + /* Responsive images (ensure images don't scale beyond their parents) */ + max-width: 100%; + /* Part 1: Set a maxium relative to the parent */ + width: auto\9; + /* IE7-8 need help adjusting responsive images */ + height: auto; + /* Part 2: Scale the height according to the width, otherwise you get stretching */ + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; +} +#map_canvas img, +.google-maps img { + max-width: none; +} +button, +input, +select, +textarea { + margin: 0; + font-size: 100%; + vertical-align: middle; +} +button, +input { + *overflow: visible; + line-height: normal; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +label, +select, +button, +input[type="button"], +input[type="reset"], +input[type="submit"], +input[type="radio"], +input[type="checkbox"] { + cursor: pointer; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; +} +textarea { + overflow: auto; + vertical-align: top; +} +@media print { + * { + text-shadow: none !important; + color: #000 !important; + background: transparent !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + @page { + margin: 0.5cm; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } +} +body { + margin: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 20px; + color: #333333; + background-color: #ffffff; +} +a { + color: #0088cc; + text-decoration: none; +} +a:hover, +a:focus { + color: #005580; + text-decoration: underline; +} +.img-rounded { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.img-polaroid { + padding: 4px; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} +.img-circle { + -webkit-border-radius: 500px; + -moz-border-radius: 500px; + border-radius: 500px; +} +.row { + margin-left: -20px; + *zoom: 1; +} +.row:before, +.row:after { + display: table; + content: ""; + line-height: 0; +} +.row:after { + clear: both; +} +[class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; +} +.container, +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} +.span12 { + width: 940px; +} +.span11 { + width: 860px; +} +.span10 { + width: 780px; +} +.span9 { + width: 700px; +} +.span8 { + width: 620px; +} +.span7 { + width: 540px; +} +.span6 { + width: 460px; +} +.span5 { + width: 380px; +} +.span4 { + width: 300px; +} +.span3 { + width: 220px; +} +.span2 { + width: 140px; +} +.span1 { + width: 60px; +} +.offset12 { + margin-left: 980px; +} +.offset11 { + margin-left: 900px; +} +.offset10 { + margin-left: 820px; +} +.offset9 { + margin-left: 740px; +} +.offset8 { + margin-left: 660px; +} +.offset7 { + margin-left: 580px; +} +.offset6 { + margin-left: 500px; +} +.offset5 { + margin-left: 420px; +} +.offset4 { + margin-left: 340px; +} +.offset3 { + margin-left: 260px; +} +.offset2 { + margin-left: 180px; +} +.offset1 { + margin-left: 100px; +} +.row-fluid { + width: 100%; + *zoom: 1; +} +.row-fluid:before, +.row-fluid:after { + display: table; + content: ""; + line-height: 0; +} +.row-fluid:after { + clear: both; +} +.row-fluid [class*="span"] { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + float: left; + margin-left: 2.12765957%; + *margin-left: 2.07446809%; +} +.row-fluid [class*="span"]:first-child { + margin-left: 0; +} +.row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.12765957%; +} +.row-fluid .span12 { + width: 100%; + *width: 99.94680851%; +} +.row-fluid .span11 { + width: 91.4893617%; + *width: 91.43617021%; +} +.row-fluid .span10 { + width: 82.9787234%; + *width: 82.92553191%; +} +.row-fluid .span9 { + width: 74.46808511%; + *width: 74.41489362%; +} +.row-fluid .span8 { + width: 65.95744681%; + *width: 65.90425532%; +} +.row-fluid .span7 { + width: 57.44680851%; + *width: 57.39361702%; +} +.row-fluid .span6 { + width: 48.93617021%; + *width: 48.88297872%; +} +.row-fluid .span5 { + width: 40.42553191%; + *width: 40.37234043%; +} +.row-fluid .span4 { + width: 31.91489362%; + *width: 31.86170213%; +} +.row-fluid .span3 { + width: 23.40425532%; + *width: 23.35106383%; +} +.row-fluid .span2 { + width: 14.89361702%; + *width: 14.84042553%; +} +.row-fluid .span1 { + width: 6.38297872%; + *width: 6.32978723%; +} +.row-fluid .offset12 { + margin-left: 104.25531915%; + *margin-left: 104.14893617%; +} +.row-fluid .offset12:first-child { + margin-left: 102.12765957%; + *margin-left: 102.0212766%; +} +.row-fluid .offset11 { + margin-left: 95.74468085%; + *margin-left: 95.63829787%; +} +.row-fluid .offset11:first-child { + margin-left: 93.61702128%; + *margin-left: 93.5106383%; +} +.row-fluid .offset10 { + margin-left: 87.23404255%; + *margin-left: 87.12765957%; +} +.row-fluid .offset10:first-child { + margin-left: 85.10638298%; + *margin-left: 85%; +} +.row-fluid .offset9 { + margin-left: 78.72340426%; + *margin-left: 78.61702128%; +} +.row-fluid .offset9:first-child { + margin-left: 76.59574468%; + *margin-left: 76.4893617%; +} +.row-fluid .offset8 { + margin-left: 70.21276596%; + *margin-left: 70.10638298%; +} +.row-fluid .offset8:first-child { + margin-left: 68.08510638%; + *margin-left: 67.9787234%; +} +.row-fluid .offset7 { + margin-left: 61.70212766%; + *margin-left: 61.59574468%; +} +.row-fluid .offset7:first-child { + margin-left: 59.57446809%; + *margin-left: 59.46808511%; +} +.row-fluid .offset6 { + margin-left: 53.19148936%; + *margin-left: 53.08510638%; +} +.row-fluid .offset6:first-child { + margin-left: 51.06382979%; + *margin-left: 50.95744681%; +} +.row-fluid .offset5 { + margin-left: 44.68085106%; + *margin-left: 44.57446809%; +} +.row-fluid .offset5:first-child { + margin-left: 42.55319149%; + *margin-left: 42.44680851%; +} +.row-fluid .offset4 { + margin-left: 36.17021277%; + *margin-left: 36.06382979%; +} +.row-fluid .offset4:first-child { + margin-left: 34.04255319%; + *margin-left: 33.93617021%; +} +.row-fluid .offset3 { + margin-left: 27.65957447%; + *margin-left: 27.55319149%; +} +.row-fluid .offset3:first-child { + margin-left: 25.53191489%; + *margin-left: 25.42553191%; +} +.row-fluid .offset2 { + margin-left: 19.14893617%; + *margin-left: 19.04255319%; +} +.row-fluid .offset2:first-child { + margin-left: 17.0212766%; + *margin-left: 16.91489362%; +} +.row-fluid .offset1 { + margin-left: 10.63829787%; + *margin-left: 10.53191489%; +} +.row-fluid .offset1:first-child { + margin-left: 8.5106383%; + *margin-left: 8.40425532%; +} +[class*="span"].hide, +.row-fluid [class*="span"].hide { + display: none; +} +[class*="span"].pull-right, +.row-fluid [class*="span"].pull-right { + float: right; +} +.container { + margin-right: auto; + margin-left: auto; + *zoom: 1; +} +.container:before, +.container:after { + display: table; + content: ""; + line-height: 0; +} +.container:after { + clear: both; +} +.container-fluid { + padding-right: 20px; + padding-left: 20px; + *zoom: 1; +} +.container-fluid:before, +.container-fluid:after { + display: table; + content: ""; + line-height: 0; +} +.container-fluid:after { + clear: both; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 21px; + font-weight: 200; + line-height: 30px; +} +small { + font-size: 85%; +} +strong { + font-weight: bold; +} +em { + font-style: italic; +} +cite { + font-style: normal; +} +.muted { + color: #999999; +} +a.muted:hover, +a.muted:focus { + color: #808080; +} +.text-warning { + color: #c09853; +} +a.text-warning:hover, +a.text-warning:focus { + color: #a47e3c; +} +.text-error { + color: #b94a48; +} +a.text-error:hover, +a.text-error:focus { + color: #953b39; +} +.text-info { + color: #3a87ad; +} +a.text-info:hover, +a.text-info:focus { + color: #2d6987; +} +.text-success { + color: #468847; +} +a.text-success:hover, +a.text-success:focus { + color: #356635; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 10px 0; + font-family: inherit; + font-weight: bold; + line-height: 20px; + color: inherit; + text-rendering: optimizelegibility; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small { + font-weight: normal; + line-height: 1; + color: #999999; +} +h1, +h2, +h3 { + line-height: 40px; +} +h1 { + font-size: 38.5px; +} +h2 { + font-size: 31.5px; +} +h3 { + font-size: 24.5px; +} +h4 { + font-size: 17.5px; +} +h5 { + font-size: 14px; +} +h6 { + font-size: 11.9px; +} +h1 small { + font-size: 24.5px; +} +h2 small { + font-size: 17.5px; +} +h3 small { + font-size: 14px; +} +h4 small { + font-size: 14px; +} +.page-header { + padding-bottom: 9px; + margin: 20px 0 30px; + border-bottom: 1px solid #eeeeee; +} +ul, +ol { + padding: 0; + margin: 0 0 10px 25px; +} +ul ul, +ul ol, +ol ol, +ol ul { + margin-bottom: 0; +} +li { + line-height: 20px; +} +ul.unstyled, +ol.unstyled { + margin-left: 0; + list-style: none; +} +ul.inline, +ol.inline { + margin-left: 0; + list-style: none; +} +ul.inline > li, +ol.inline > li { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + padding-left: 5px; + padding-right: 5px; +} +dl { + margin-bottom: 20px; +} +dt, +dd { + line-height: 20px; +} +dt { + font-weight: bold; +} +dd { + margin-left: 10px; +} +.dl-horizontal { + *zoom: 1; +} +.dl-horizontal:before, +.dl-horizontal:after { + display: table; + content: ""; + line-height: 0; +} +.dl-horizontal:after { + clear: both; +} +.dl-horizontal dt { + float: left; + width: 160px; + clear: left; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.dl-horizontal dd { + margin-left: 180px; +} +hr { + margin: 20px 0; + border: 0; + border-top: 1px solid #eeeeee; + border-bottom: 1px solid #ffffff; +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999999; +} +abbr.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 0 0 0 15px; + margin: 0 0 20px; + border-left: 5px solid #eeeeee; +} +blockquote p { + margin-bottom: 0; + font-size: 17.5px; + font-weight: 300; + line-height: 1.25; +} +blockquote small { + display: block; + line-height: 20px; + color: #999999; +} +blockquote small:before { + content: '\2014 \00A0'; +} +blockquote.pull-right { + float: right; + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #eeeeee; + border-left: 0; +} +blockquote.pull-right p, +blockquote.pull-right small { + text-align: right; +} +blockquote.pull-right small:before { + content: ''; +} +blockquote.pull-right small:after { + content: '\00A0 \2014'; +} +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; +} +address { + display: block; + margin-bottom: 20px; + font-style: normal; + line-height: 20px; +} +code, +pre { + padding: 0 3px 2px; + font-family: Monaco, Menlo, Consolas, "Courier New", monospace; + font-size: 12px; + color: #333333; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +code { + padding: 2px 4px; + color: #d14; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; + white-space: nowrap; +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 20px; + word-break: break-all; + word-wrap: break-word; + white-space: pre; + white-space: pre-wrap; + background-color: #f5f5f5; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +pre.prettyprint { + margin-bottom: 20px; +} +pre code { + padding: 0; + color: inherit; + white-space: pre; + white-space: pre-wrap; + background-color: transparent; + border: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +form { + margin: 0 0 20px; +} +fieldset { + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: 40px; + color: #333333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +legend small { + font-size: 15px; + color: #999999; +} +label, +input, +button, +select, +textarea { + font-size: 14px; + font-weight: normal; + line-height: 20px; +} +input, +button, +select, +textarea { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} +label { + display: block; + margin-bottom: 5px; +} +select, +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + display: inline-block; + height: 20px; + padding: 4px 6px; + margin-bottom: 10px; + font-size: 14px; + line-height: 20px; + color: #555555; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + vertical-align: middle; +} +input, +textarea, +.uneditable-input { + width: 206px; +} +textarea { + height: auto; +} +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + background-color: #ffffff; + border: 1px solid #cccccc; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border linear .2s, box-shadow linear .2s; + -moz-transition: border linear .2s, box-shadow linear .2s; + -o-transition: border linear .2s, box-shadow linear .2s; + transition: border linear .2s, box-shadow linear .2s; +} +textarea:focus, +input[type="text"]:focus, +input[type="password"]:focus, +input[type="datetime"]:focus, +input[type="datetime-local"]:focus, +input[type="date"]:focus, +input[type="month"]:focus, +input[type="time"]:focus, +input[type="week"]:focus, +input[type="number"]:focus, +input[type="email"]:focus, +input[type="url"]:focus, +input[type="search"]:focus, +input[type="tel"]:focus, +input[type="color"]:focus, +.uneditable-input:focus { + border-color: rgba(82, 168, 236, 0.8); + outline: 0; + outline: thin dotted \9; + /* IE6-9 */ + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); + -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + *margin-top: 0; + /* IE7 */ + margin-top: 1px \9; + /* IE8-9 */ + line-height: normal; +} +input[type="file"], +input[type="image"], +input[type="submit"], +input[type="reset"], +input[type="button"], +input[type="radio"], +input[type="checkbox"] { + width: auto; +} +select, +input[type="file"] { + height: 30px; + /* In IE7, the height of the select element cannot be changed by height, only font-size */ + *margin-top: 4px; + /* For IE7, add top margin to align select with labels */ + line-height: 30px; +} +select { + width: 220px; + border: 1px solid #cccccc; + background-color: #ffffff; +} +select[multiple], +select[size] { + height: auto; +} +select:focus, +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.uneditable-input, +.uneditable-textarea { + color: #999999; + background-color: #fcfcfc; + border-color: #cccccc; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + cursor: not-allowed; +} +.uneditable-input { + overflow: hidden; + white-space: nowrap; +} +.uneditable-textarea { + width: auto; + height: auto; +} +input:-moz-placeholder, +textarea:-moz-placeholder { + color: #999999; +} +input:-ms-input-placeholder, +textarea:-ms-input-placeholder { + color: #999999; +} +input::-webkit-input-placeholder, +textarea::-webkit-input-placeholder { + color: #999999; +} +.radio, +.checkbox { + min-height: 20px; + padding-left: 20px; +} +.radio input[type="radio"], +.checkbox input[type="checkbox"] { + float: left; + margin-left: -20px; +} +.controls > .radio:first-child, +.controls > .checkbox:first-child { + padding-top: 5px; +} +.radio.inline, +.checkbox.inline { + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} +.radio.inline + .radio.inline, +.checkbox.inline + .checkbox.inline { + margin-left: 10px; +} +.input-mini { + width: 60px; +} +.input-small { + width: 90px; +} +.input-medium { + width: 150px; +} +.input-large { + width: 210px; +} +.input-xlarge { + width: 270px; +} +.input-xxlarge { + width: 530px; +} +input[class*="span"], +select[class*="span"], +textarea[class*="span"], +.uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"] { + float: none; + margin-left: 0; +} +.input-append input[class*="span"], +.input-append .uneditable-input[class*="span"], +.input-prepend input[class*="span"], +.input-prepend .uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"], +.row-fluid .input-prepend [class*="span"], +.row-fluid .input-append [class*="span"] { + display: inline-block; +} +input, +textarea, +.uneditable-input { + margin-left: 0; +} +.controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; +} +input.span12, +textarea.span12, +.uneditable-input.span12 { + width: 926px; +} +input.span11, +textarea.span11, +.uneditable-input.span11 { + width: 846px; +} +input.span10, +textarea.span10, +.uneditable-input.span10 { + width: 766px; +} +input.span9, +textarea.span9, +.uneditable-input.span9 { + width: 686px; +} +input.span8, +textarea.span8, +.uneditable-input.span8 { + width: 606px; +} +input.span7, +textarea.span7, +.uneditable-input.span7 { + width: 526px; +} +input.span6, +textarea.span6, +.uneditable-input.span6 { + width: 446px; +} +input.span5, +textarea.span5, +.uneditable-input.span5 { + width: 366px; +} +input.span4, +textarea.span4, +.uneditable-input.span4 { + width: 286px; +} +input.span3, +textarea.span3, +.uneditable-input.span3 { + width: 206px; +} +input.span2, +textarea.span2, +.uneditable-input.span2 { + width: 126px; +} +input.span1, +textarea.span1, +.uneditable-input.span1 { + width: 46px; +} +.controls-row { + *zoom: 1; +} +.controls-row:before, +.controls-row:after { + display: table; + content: ""; + line-height: 0; +} +.controls-row:after { + clear: both; +} +.controls-row [class*="span"], +.row-fluid .controls-row [class*="span"] { + float: left; +} +.controls-row .checkbox[class*="span"], +.controls-row .radio[class*="span"] { + padding-top: 5px; +} +input[disabled], +select[disabled], +textarea[disabled], +input[readonly], +select[readonly], +textarea[readonly] { + cursor: not-allowed; + background-color: #eeeeee; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"][readonly], +input[type="checkbox"][readonly] { + background-color: transparent; +} +.control-group.warning .control-label, +.control-group.warning .help-block, +.control-group.warning .help-inline { + color: #c09853; +} +.control-group.warning .checkbox, +.control-group.warning .radio, +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + color: #c09853; +} +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + border-color: #c09853; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.control-group.warning input:focus, +.control-group.warning select:focus, +.control-group.warning textarea:focus { + border-color: #a47e3c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; +} +.control-group.warning .input-prepend .add-on, +.control-group.warning .input-append .add-on { + color: #c09853; + background-color: #fcf8e3; + border-color: #c09853; +} +.control-group.error .control-label, +.control-group.error .help-block, +.control-group.error .help-inline { + color: #b94a48; +} +.control-group.error .checkbox, +.control-group.error .radio, +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + color: #b94a48; +} +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + border-color: #b94a48; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.control-group.error input:focus, +.control-group.error select:focus, +.control-group.error textarea:focus { + border-color: #953b39; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; +} +.control-group.error .input-prepend .add-on, +.control-group.error .input-append .add-on { + color: #b94a48; + background-color: #f2dede; + border-color: #b94a48; +} +.control-group.success .control-label, +.control-group.success .help-block, +.control-group.success .help-inline { + color: #468847; +} +.control-group.success .checkbox, +.control-group.success .radio, +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + color: #468847; +} +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + border-color: #468847; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.control-group.success input:focus, +.control-group.success select:focus, +.control-group.success textarea:focus { + border-color: #356635; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; +} +.control-group.success .input-prepend .add-on, +.control-group.success .input-append .add-on { + color: #468847; + background-color: #dff0d8; + border-color: #468847; +} +.control-group.info .control-label, +.control-group.info .help-block, +.control-group.info .help-inline { + color: #3a87ad; +} +.control-group.info .checkbox, +.control-group.info .radio, +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + color: #3a87ad; +} +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + border-color: #3a87ad; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.control-group.info input:focus, +.control-group.info select:focus, +.control-group.info textarea:focus { + border-color: #2d6987; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; +} +.control-group.info .input-prepend .add-on, +.control-group.info .input-append .add-on { + color: #3a87ad; + background-color: #d9edf7; + border-color: #3a87ad; +} +input:focus:invalid, +textarea:focus:invalid, +select:focus:invalid { + color: #b94a48; + border-color: #ee5f5b; +} +input:focus:invalid:focus, +textarea:focus:invalid:focus, +select:focus:invalid:focus { + border-color: #e9322d; + -webkit-box-shadow: 0 0 6px #f8b9b7; + -moz-box-shadow: 0 0 6px #f8b9b7; + box-shadow: 0 0 6px #f8b9b7; +} +.form-actions { + padding: 19px 20px 20px; + margin-top: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-top: 1px solid #e5e5e5; + *zoom: 1; +} +.form-actions:before, +.form-actions:after { + display: table; + content: ""; + line-height: 0; +} +.form-actions:after { + clear: both; +} +.help-block, +.help-inline { + color: #595959; +} +.help-block { + display: block; + margin-bottom: 10px; +} +.help-inline { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + vertical-align: middle; + padding-left: 5px; +} +.input-append, +.input-prepend { + display: inline-block; + margin-bottom: 10px; + vertical-align: middle; + font-size: 0; + white-space: nowrap; +} +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input, +.input-append .dropdown-menu, +.input-prepend .dropdown-menu, +.input-append .popover, +.input-prepend .popover { + font-size: 14px; +} +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input { + position: relative; + margin-bottom: 0; + *margin-left: 0; + vertical-align: top; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.input-append input:focus, +.input-prepend input:focus, +.input-append select:focus, +.input-prepend select:focus, +.input-append .uneditable-input:focus, +.input-prepend .uneditable-input:focus { + z-index: 2; +} +.input-append .add-on, +.input-prepend .add-on { + display: inline-block; + width: auto; + height: 20px; + min-width: 16px; + padding: 4px 5px; + font-size: 14px; + font-weight: normal; + line-height: 20px; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + background-color: #eeeeee; + border: 1px solid #ccc; +} +.input-append .add-on, +.input-prepend .add-on, +.input-append .btn, +.input-prepend .btn, +.input-append .btn-group > .dropdown-toggle, +.input-prepend .btn-group > .dropdown-toggle { + vertical-align: top; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.input-append .active, +.input-prepend .active { + background-color: #a9dba9; + border-color: #46a546; +} +.input-prepend .add-on, +.input-prepend .btn { + margin-right: -1px; +} +.input-prepend .add-on:first-child, +.input-prepend .btn:first-child { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +.input-append input, +.input-append select, +.input-append .uneditable-input { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +.input-append input + .btn-group .btn:last-child, +.input-append select + .btn-group .btn:last-child, +.input-append .uneditable-input + .btn-group .btn:last-child { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.input-append .add-on, +.input-append .btn, +.input-append .btn-group { + margin-left: -1px; +} +.input-append .add-on:last-child, +.input-append .btn:last-child, +.input-append .btn-group:last-child > .dropdown-toggle { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.input-prepend.input-append input, +.input-prepend.input-append select, +.input-prepend.input-append .uneditable-input { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.input-prepend.input-append input + .btn-group .btn, +.input-prepend.input-append select + .btn-group .btn, +.input-prepend.input-append .uneditable-input + .btn-group .btn { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.input-prepend.input-append .add-on:first-child, +.input-prepend.input-append .btn:first-child { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +.input-prepend.input-append .add-on:last-child, +.input-prepend.input-append .btn:last-child { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.input-prepend.input-append .btn-group:first-child { + margin-left: 0; +} +input.search-query { + padding-right: 14px; + padding-right: 4px \9; + padding-left: 14px; + padding-left: 4px \9; + /* IE7-8 doesn't have border-radius, so don't indent the padding */ + margin-bottom: 0; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} +/* Allow for input prepend/append in search forms */ +.form-search .input-append .search-query, +.form-search .input-prepend .search-query { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.form-search .input-append .search-query { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} +.form-search .input-append .btn { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} +.form-search .input-prepend .search-query { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} +.form-search .input-prepend .btn { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} +.form-search input, +.form-inline input, +.form-horizontal input, +.form-search textarea, +.form-inline textarea, +.form-horizontal textarea, +.form-search select, +.form-inline select, +.form-horizontal select, +.form-search .help-inline, +.form-inline .help-inline, +.form-horizontal .help-inline, +.form-search .uneditable-input, +.form-inline .uneditable-input, +.form-horizontal .uneditable-input, +.form-search .input-prepend, +.form-inline .input-prepend, +.form-horizontal .input-prepend, +.form-search .input-append, +.form-inline .input-append, +.form-horizontal .input-append { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + margin-bottom: 0; + vertical-align: middle; +} +.form-search .hide, +.form-inline .hide, +.form-horizontal .hide { + display: none; +} +.form-search label, +.form-inline label, +.form-search .btn-group, +.form-inline .btn-group { + display: inline-block; +} +.form-search .input-append, +.form-inline .input-append, +.form-search .input-prepend, +.form-inline .input-prepend { + margin-bottom: 0; +} +.form-search .radio, +.form-search .checkbox, +.form-inline .radio, +.form-inline .checkbox { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} +.form-search .radio input[type="radio"], +.form-search .checkbox input[type="checkbox"], +.form-inline .radio input[type="radio"], +.form-inline .checkbox input[type="checkbox"] { + float: left; + margin-right: 3px; + margin-left: 0; +} +.control-group { + margin-bottom: 10px; +} +legend + .control-group { + margin-top: 20px; + -webkit-margin-top-collapse: separate; +} +.form-horizontal .control-group { + margin-bottom: 20px; + *zoom: 1; +} +.form-horizontal .control-group:before, +.form-horizontal .control-group:after { + display: table; + content: ""; + line-height: 0; +} +.form-horizontal .control-group:after { + clear: both; +} +.form-horizontal .control-label { + float: left; + width: 160px; + padding-top: 5px; + text-align: right; +} +.form-horizontal .controls { + *display: inline-block; + *padding-left: 20px; + margin-left: 180px; + *margin-left: 0; +} +.form-horizontal .controls:first-child { + *padding-left: 180px; +} +.form-horizontal .help-block { + margin-bottom: 0; +} +.form-horizontal input + .help-block, +.form-horizontal select + .help-block, +.form-horizontal textarea + .help-block, +.form-horizontal .uneditable-input + .help-block, +.form-horizontal .input-prepend + .help-block, +.form-horizontal .input-append + .help-block { + margin-top: 10px; +} +.form-horizontal .form-actions { + padding-left: 180px; +} +table { + max-width: 100%; + background-color: transparent; + border-collapse: collapse; + border-spacing: 0; +} +.table { + width: 100%; + margin-bottom: 20px; +} +.table th, +.table td { + padding: 8px; + line-height: 20px; + text-align: left; + vertical-align: top; + border-top: 1px solid #dddddd; +} +.table th { + font-weight: bold; +} +.table thead th { + vertical-align: bottom; +} +.table caption + thead tr:first-child th, +.table caption + thead tr:first-child td, +.table colgroup + thead tr:first-child th, +.table colgroup + thead tr:first-child td, +.table thead:first-child tr:first-child th, +.table thead:first-child tr:first-child td { + border-top: 0; +} +.table tbody + tbody { + border-top: 2px solid #dddddd; +} +.table .table { + background-color: #ffffff; +} +.table-condensed th, +.table-condensed td { + padding: 4px 5px; +} +.table-bordered { + border: 1px solid #dddddd; + border-collapse: separate; + *border-collapse: collapse; + border-left: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.table-bordered th, +.table-bordered td { + border-left: 1px solid #dddddd; +} +.table-bordered caption + thead tr:first-child th, +.table-bordered caption + tbody tr:first-child th, +.table-bordered caption + tbody tr:first-child td, +.table-bordered colgroup + thead tr:first-child th, +.table-bordered colgroup + tbody tr:first-child th, +.table-bordered colgroup + tbody tr:first-child td, +.table-bordered thead:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child td { + border-top: 0; +} +.table-bordered thead:first-child tr:first-child > th:first-child, +.table-bordered tbody:first-child tr:first-child > td:first-child, +.table-bordered tbody:first-child tr:first-child > th:first-child { + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; +} +.table-bordered thead:first-child tr:first-child > th:last-child, +.table-bordered tbody:first-child tr:first-child > td:last-child, +.table-bordered tbody:first-child tr:first-child > th:last-child { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; +} +.table-bordered thead:last-child tr:last-child > th:first-child, +.table-bordered tbody:last-child tr:last-child > td:first-child, +.table-bordered tbody:last-child tr:last-child > th:first-child, +.table-bordered tfoot:last-child tr:last-child > td:first-child, +.table-bordered tfoot:last-child tr:last-child > th:first-child { + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; +} +.table-bordered thead:last-child tr:last-child > th:last-child, +.table-bordered tbody:last-child tr:last-child > td:last-child, +.table-bordered tbody:last-child tr:last-child > th:last-child, +.table-bordered tfoot:last-child tr:last-child > td:last-child, +.table-bordered tfoot:last-child tr:last-child > th:last-child { + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; +} +.table-bordered tfoot + tbody:last-child tr:last-child td:first-child { + -webkit-border-bottom-left-radius: 0; + -moz-border-radius-bottomleft: 0; + border-bottom-left-radius: 0; +} +.table-bordered tfoot + tbody:last-child tr:last-child td:last-child { + -webkit-border-bottom-right-radius: 0; + -moz-border-radius-bottomright: 0; + border-bottom-right-radius: 0; +} +.table-bordered caption + thead tr:first-child th:first-child, +.table-bordered caption + tbody tr:first-child td:first-child, +.table-bordered colgroup + thead tr:first-child th:first-child, +.table-bordered colgroup + tbody tr:first-child td:first-child { + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; +} +.table-bordered caption + thead tr:first-child th:last-child, +.table-bordered caption + tbody tr:first-child td:last-child, +.table-bordered colgroup + thead tr:first-child th:last-child, +.table-bordered colgroup + tbody tr:first-child td:last-child { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; +} +.table-striped tbody > tr:nth-child(odd) > td, +.table-striped tbody > tr:nth-child(odd) > th { + background-color: #f9f9f9; +} +.table-hover tbody tr:hover > td, +.table-hover tbody tr:hover > th { + background-color: #f5f5f5; +} +table td[class*="span"], +table th[class*="span"], +.row-fluid table td[class*="span"], +.row-fluid table th[class*="span"] { + display: table-cell; + float: none; + margin-left: 0; +} +.table td.span1, +.table th.span1 { + float: none; + width: 44px; + margin-left: 0; +} +.table td.span2, +.table th.span2 { + float: none; + width: 124px; + margin-left: 0; +} +.table td.span3, +.table th.span3 { + float: none; + width: 204px; + margin-left: 0; +} +.table td.span4, +.table th.span4 { + float: none; + width: 284px; + margin-left: 0; +} +.table td.span5, +.table th.span5 { + float: none; + width: 364px; + margin-left: 0; +} +.table td.span6, +.table th.span6 { + float: none; + width: 444px; + margin-left: 0; +} +.table td.span7, +.table th.span7 { + float: none; + width: 524px; + margin-left: 0; +} +.table td.span8, +.table th.span8 { + float: none; + width: 604px; + margin-left: 0; +} +.table td.span9, +.table th.span9 { + float: none; + width: 684px; + margin-left: 0; +} +.table td.span10, +.table th.span10 { + float: none; + width: 764px; + margin-left: 0; +} +.table td.span11, +.table th.span11 { + float: none; + width: 844px; + margin-left: 0; +} +.table td.span12, +.table th.span12 { + float: none; + width: 924px; + margin-left: 0; +} +.table tbody tr.success > td { + background-color: #dff0d8; +} +.table tbody tr.error > td { + background-color: #f2dede; +} +.table tbody tr.warning > td { + background-color: #fcf8e3; +} +.table tbody tr.info > td { + background-color: #d9edf7; +} +.table-hover tbody tr.success:hover > td { + background-color: #d0e9c6; +} +.table-hover tbody tr.error:hover > td { + background-color: #ebcccc; +} +.table-hover tbody tr.warning:hover > td { + background-color: #faf2cc; +} +.table-hover tbody tr.info:hover > td { + background-color: #c4e3f3; +} +[class^="icon-"], +[class*=" icon-"] { + display: inline-block; + width: 14px; + height: 14px; + *margin-right: .3em; + line-height: 14px; + vertical-align: text-top; + background-image: url("../img/glyphicons-halflings.png"); + background-position: 14px 14px; + background-repeat: no-repeat; + margin-top: 1px; +} +/* White icons with optional class, or on hover/focus/active states of certain elements */ +.icon-white, +.nav-pills > .active > a > [class^="icon-"], +.nav-pills > .active > a > [class*=" icon-"], +.nav-list > .active > a > [class^="icon-"], +.nav-list > .active > a > [class*=" icon-"], +.navbar-inverse .nav > .active > a > [class^="icon-"], +.navbar-inverse .nav > .active > a > [class*=" icon-"], +.dropdown-menu > li > a:hover > [class^="icon-"], +.dropdown-menu > li > a:focus > [class^="icon-"], +.dropdown-menu > li > a:hover > [class*=" icon-"], +.dropdown-menu > li > a:focus > [class*=" icon-"], +.dropdown-menu > .active > a > [class^="icon-"], +.dropdown-menu > .active > a > [class*=" icon-"], +.dropdown-submenu:hover > a > [class^="icon-"], +.dropdown-submenu:focus > a > [class^="icon-"], +.dropdown-submenu:hover > a > [class*=" icon-"], +.dropdown-submenu:focus > a > [class*=" icon-"] { + background-image: url("../img/glyphicons-halflings-white.png"); +} +.icon-glass { + background-position: 0 0; +} +.icon-music { + background-position: -24px 0; +} +.icon-search { + background-position: -48px 0; +} +.icon-envelope { + background-position: -72px 0; +} +.icon-heart { + background-position: -96px 0; +} +.icon-star { + background-position: -120px 0; +} +.icon-star-empty { + background-position: -144px 0; +} +.icon-user { + background-position: -168px 0; +} +.icon-film { + background-position: -192px 0; +} +.icon-th-large { + background-position: -216px 0; +} +.icon-th { + background-position: -240px 0; +} +.icon-th-list { + background-position: -264px 0; +} +.icon-ok { + background-position: -288px 0; +} +.icon-remove { + background-position: -312px 0; +} +.icon-zoom-in { + background-position: -336px 0; +} +.icon-zoom-out { + background-position: -360px 0; +} +.icon-off { + background-position: -384px 0; +} +.icon-signal { + background-position: -408px 0; +} +.icon-cog { + background-position: -432px 0; +} +.icon-trash { + background-position: -456px 0; +} +.icon-home { + background-position: 0 -24px; +} +.icon-file { + background-position: -24px -24px; +} +.icon-time { + background-position: -48px -24px; +} +.icon-road { + background-position: -72px -24px; +} +.icon-download-alt { + background-position: -96px -24px; +} +.icon-download { + background-position: -120px -24px; +} +.icon-upload { + background-position: -144px -24px; +} +.icon-inbox { + background-position: -168px -24px; +} +.icon-play-circle { + background-position: -192px -24px; +} +.icon-repeat { + background-position: -216px -24px; +} +.icon-refresh { + background-position: -240px -24px; +} +.icon-list-alt { + background-position: -264px -24px; +} +.icon-lock { + background-position: -287px -24px; +} +.icon-flag { + background-position: -312px -24px; +} +.icon-headphones { + background-position: -336px -24px; +} +.icon-volume-off { + background-position: -360px -24px; +} +.icon-volume-down { + background-position: -384px -24px; +} +.icon-volume-up { + background-position: -408px -24px; +} +.icon-qrcode { + background-position: -432px -24px; +} +.icon-barcode { + background-position: -456px -24px; +} +.icon-tag { + background-position: 0 -48px; +} +.icon-tags { + background-position: -25px -48px; +} +.icon-book { + background-position: -48px -48px; +} +.icon-bookmark { + background-position: -72px -48px; +} +.icon-print { + background-position: -96px -48px; +} +.icon-camera { + background-position: -120px -48px; +} +.icon-font { + background-position: -144px -48px; +} +.icon-bold { + background-position: -167px -48px; +} +.icon-italic { + background-position: -192px -48px; +} +.icon-text-height { + background-position: -216px -48px; +} +.icon-text-width { + background-position: -240px -48px; +} +.icon-align-left { + background-position: -264px -48px; +} +.icon-align-center { + background-position: -288px -48px; +} +.icon-align-right { + background-position: -312px -48px; +} +.icon-align-justify { + background-position: -336px -48px; +} +.icon-list { + background-position: -360px -48px; +} +.icon-indent-left { + background-position: -384px -48px; +} +.icon-indent-right { + background-position: -408px -48px; +} +.icon-facetime-video { + background-position: -432px -48px; +} +.icon-picture { + background-position: -456px -48px; +} +.icon-pencil { + background-position: 0 -72px; +} +.icon-map-marker { + background-position: -24px -72px; +} +.icon-adjust { + background-position: -48px -72px; +} +.icon-tint { + background-position: -72px -72px; +} +.icon-edit { + background-position: -96px -72px; +} +.icon-share { + background-position: -120px -72px; +} +.icon-check { + background-position: -144px -72px; +} +.icon-move { + background-position: -168px -72px; +} +.icon-step-backward { + background-position: -192px -72px; +} +.icon-fast-backward { + background-position: -216px -72px; +} +.icon-backward { + background-position: -240px -72px; +} +.icon-play { + background-position: -264px -72px; +} +.icon-pause { + background-position: -288px -72px; +} +.icon-stop { + background-position: -312px -72px; +} +.icon-forward { + background-position: -336px -72px; +} +.icon-fast-forward { + background-position: -360px -72px; +} +.icon-step-forward { + background-position: -384px -72px; +} +.icon-eject { + background-position: -408px -72px; +} +.icon-chevron-left { + background-position: -432px -72px; +} +.icon-chevron-right { + background-position: -456px -72px; +} +.icon-plus-sign { + background-position: 0 -96px; +} +.icon-minus-sign { + background-position: -24px -96px; +} +.icon-remove-sign { + background-position: -48px -96px; +} +.icon-ok-sign { + background-position: -72px -96px; +} +.icon-question-sign { + background-position: -96px -96px; +} +.icon-info-sign { + background-position: -120px -96px; +} +.icon-screenshot { + background-position: -144px -96px; +} +.icon-remove-circle { + background-position: -168px -96px; +} +.icon-ok-circle { + background-position: -192px -96px; +} +.icon-ban-circle { + background-position: -216px -96px; +} +.icon-arrow-left { + background-position: -240px -96px; +} +.icon-arrow-right { + background-position: -264px -96px; +} +.icon-arrow-up { + background-position: -289px -96px; +} +.icon-arrow-down { + background-position: -312px -96px; +} +.icon-share-alt { + background-position: -336px -96px; +} +.icon-resize-full { + background-position: -360px -96px; +} +.icon-resize-small { + background-position: -384px -96px; +} +.icon-plus { + background-position: -408px -96px; +} +.icon-minus { + background-position: -433px -96px; +} +.icon-asterisk { + background-position: -456px -96px; +} +.icon-exclamation-sign { + background-position: 0 -120px; +} +.icon-gift { + background-position: -24px -120px; +} +.icon-leaf { + background-position: -48px -120px; +} +.icon-fire { + background-position: -72px -120px; +} +.icon-eye-open { + background-position: -96px -120px; +} +.icon-eye-close { + background-position: -120px -120px; +} +.icon-warning-sign { + background-position: -144px -120px; +} +.icon-plane { + background-position: -168px -120px; +} +.icon-calendar { + background-position: -192px -120px; +} +.icon-random { + background-position: -216px -120px; + width: 16px; +} +.icon-comment { + background-position: -240px -120px; +} +.icon-magnet { + background-position: -264px -120px; +} +.icon-chevron-up { + background-position: -288px -120px; +} +.icon-chevron-down { + background-position: -313px -119px; +} +.icon-retweet { + background-position: -336px -120px; +} +.icon-shopping-cart { + background-position: -360px -120px; +} +.icon-folder-close { + background-position: -384px -120px; + width: 16px; +} +.icon-folder-open { + background-position: -408px -120px; + width: 16px; +} +.icon-resize-vertical { + background-position: -432px -119px; +} +.icon-resize-horizontal { + background-position: -456px -118px; +} +.icon-hdd { + background-position: 0 -144px; +} +.icon-bullhorn { + background-position: -24px -144px; +} +.icon-bell { + background-position: -48px -144px; +} +.icon-certificate { + background-position: -72px -144px; +} +.icon-thumbs-up { + background-position: -96px -144px; +} +.icon-thumbs-down { + background-position: -120px -144px; +} +.icon-hand-right { + background-position: -144px -144px; +} +.icon-hand-left { + background-position: -168px -144px; +} +.icon-hand-up { + background-position: -192px -144px; +} +.icon-hand-down { + background-position: -216px -144px; +} +.icon-circle-arrow-right { + background-position: -240px -144px; +} +.icon-circle-arrow-left { + background-position: -264px -144px; +} +.icon-circle-arrow-up { + background-position: -288px -144px; +} +.icon-circle-arrow-down { + background-position: -312px -144px; +} +.icon-globe { + background-position: -336px -144px; +} +.icon-wrench { + background-position: -360px -144px; +} +.icon-tasks { + background-position: -384px -144px; +} +.icon-filter { + background-position: -408px -144px; +} +.icon-briefcase { + background-position: -432px -144px; +} +.icon-fullscreen { + background-position: -456px -144px; +} +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle { + *margin-bottom: -3px; +} +.dropdown-toggle:active, +.open .dropdown-toggle { + outline: 0; +} +.caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: top; + border-top: 4px solid #000000; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + content: ""; +} +.dropdown .caret { + margin-top: 8px; + margin-left: 2px; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + background-color: #ffffff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + *border-right-width: 2px; + *border-bottom-width: 2px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 20px; + color: #333333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus, +.dropdown-submenu:hover > a, +.dropdown-submenu:focus > a { + text-decoration: none; + color: #ffffff; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #ffffff; + text-decoration: none; + outline: 0; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #999999; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + cursor: default; +} +.open { + *z-index: 1000; +} +.open > .dropdown-menu { + display: block; +} +.dropdown-backdrop { + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px solid #000000; + content: ""; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} +.dropdown-submenu { + position: relative; +} +.dropdown-submenu > .dropdown-menu { + top: 0; + left: 100%; + margin-top: -6px; + margin-left: -1px; + -webkit-border-radius: 0 6px 6px 6px; + -moz-border-radius: 0 6px 6px 6px; + border-radius: 0 6px 6px 6px; +} +.dropdown-submenu:hover > .dropdown-menu { + display: block; +} +.dropup .dropdown-submenu > .dropdown-menu { + top: auto; + bottom: 0; + margin-top: 0; + margin-bottom: -2px; + -webkit-border-radius: 5px 5px 5px 0; + -moz-border-radius: 5px 5px 5px 0; + border-radius: 5px 5px 5px 0; +} +.dropdown-submenu > a:after { + display: block; + content: " "; + float: right; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + border-width: 5px 0 5px 5px; + border-left-color: #cccccc; + margin-top: 5px; + margin-right: -10px; +} +.dropdown-submenu:hover > a:after { + border-left-color: #ffffff; +} +.dropdown-submenu.pull-left { + float: none; +} +.dropdown-submenu.pull-left > .dropdown-menu { + left: -100%; + margin-left: 10px; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} +.dropdown .dropdown-menu .nav-header { + padding-left: 20px; + padding-right: 20px; +} +.typeahead { + z-index: 1051; + margin-top: 2px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} +.well-large { + padding: 24px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.well-small { + padding: 9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + -moz-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height 0.35s ease; + -moz-transition: height 0.35s ease; + -o-transition: height 0.35s ease; + transition: height 0.35s ease; +} +.collapse.in { + height: auto; +} +.close { + float: right; + font-size: 20px; + font-weight: bold; + line-height: 20px; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} +.close:hover, +.close:focus { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.4; + filter: alpha(opacity=40); +} +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} +.btn { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + padding: 4px 12px; + margin-bottom: 0; + font-size: 14px; + line-height: 20px; + text-align: center; + vertical-align: middle; + cursor: pointer; + color: #333333; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + background-color: #f5f5f5; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #e6e6e6; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + border: 1px solid #cccccc; + *border: 0; + border-bottom-color: #b3b3b3; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + *margin-left: .3em; + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); +} +.btn:hover, +.btn:focus, +.btn:active, +.btn.active, +.btn.disabled, +.btn[disabled] { + color: #333333; + background-color: #e6e6e6; + *background-color: #d9d9d9; +} +.btn:active, +.btn.active { + background-color: #cccccc \9; +} +.btn:first-child { + *margin-left: 0; +} +.btn:hover, +.btn:focus { + color: #333333; + text-decoration: none; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} +.btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn.active, +.btn:active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); + box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); +} +.btn.disabled, +.btn[disabled] { + cursor: default; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.btn-large { + padding: 11px 19px; + font-size: 17.5px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.btn-large [class^="icon-"], +.btn-large [class*=" icon-"] { + margin-top: 4px; +} +.btn-small { + padding: 2px 10px; + font-size: 11.9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.btn-small [class^="icon-"], +.btn-small [class*=" icon-"] { + margin-top: 0; +} +.btn-mini [class^="icon-"], +.btn-mini [class*=" icon-"] { + margin-top: -1px; +} +.btn-mini { + padding: 0 6px; + font-size: 10.5px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; + padding-left: 0; + padding-right: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.btn-primary.active, +.btn-warning.active, +.btn-danger.active, +.btn-success.active, +.btn-info.active, +.btn-inverse.active { + color: rgba(255, 255, 255, 0.75); +} +.btn-primary { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #006dcc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(to bottom, #0088cc, #0044cc); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #0044cc; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.btn-primary.disabled, +.btn-primary[disabled] { + color: #ffffff; + background-color: #0044cc; + *background-color: #003bb3; +} +.btn-primary:active, +.btn-primary.active { + background-color: #003399 \9; +} +.btn-warning { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #faa732; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); + border-color: #f89406 #f89406 #ad6704; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #f89406; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-warning:hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.btn-warning.disabled, +.btn-warning[disabled] { + color: #ffffff; + background-color: #f89406; + *background-color: #df8505; +} +.btn-warning:active, +.btn-warning.active { + background-color: #c67605 \9; +} +.btn-danger { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #da4f49; + background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); + background-image: linear-gradient(to bottom, #ee5f5b, #bd362f); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0); + border-color: #bd362f #bd362f #802420; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #bd362f; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-danger:hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.btn-danger.disabled, +.btn-danger[disabled] { + color: #ffffff; + background-color: #bd362f; + *background-color: #a9302a; +} +.btn-danger:active, +.btn-danger.active { + background-color: #942a25 \9; +} +.btn-success { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #5bb75b; + background-image: -moz-linear-gradient(top, #62c462, #51a351); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); + background-image: -webkit-linear-gradient(top, #62c462, #51a351); + background-image: -o-linear-gradient(top, #62c462, #51a351); + background-image: linear-gradient(to bottom, #62c462, #51a351); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0); + border-color: #51a351 #51a351 #387038; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #51a351; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.btn-success.disabled, +.btn-success[disabled] { + color: #ffffff; + background-color: #51a351; + *background-color: #499249; +} +.btn-success:active, +.btn-success.active { + background-color: #408140 \9; +} +.btn-info { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #49afcd; + background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); + background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); + background-image: linear-gradient(to bottom, #5bc0de, #2f96b4); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0); + border-color: #2f96b4 #2f96b4 #1f6377; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #2f96b4; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-info:hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.btn-info.disabled, +.btn-info[disabled] { + color: #ffffff; + background-color: #2f96b4; + *background-color: #2a85a0; +} +.btn-info:active, +.btn-info.active { + background-color: #24748c \9; +} +.btn-inverse { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #363636; + background-image: -moz-linear-gradient(top, #444444, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); + background-image: -webkit-linear-gradient(top, #444444, #222222); + background-image: -o-linear-gradient(top, #444444, #222222); + background-image: linear-gradient(to bottom, #444444, #222222); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); + border-color: #222222 #222222 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #222222; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-inverse:hover, +.btn-inverse:focus, +.btn-inverse:active, +.btn-inverse.active, +.btn-inverse.disabled, +.btn-inverse[disabled] { + color: #ffffff; + background-color: #222222; + *background-color: #151515; +} +.btn-inverse:active, +.btn-inverse.active { + background-color: #080808 \9; +} +button.btn, +input[type="submit"].btn { + *padding-top: 3px; + *padding-bottom: 3px; +} +button.btn::-moz-focus-inner, +input[type="submit"].btn::-moz-focus-inner { + padding: 0; + border: 0; +} +button.btn.btn-large, +input[type="submit"].btn.btn-large { + *padding-top: 7px; + *padding-bottom: 7px; +} +button.btn.btn-small, +input[type="submit"].btn.btn-small { + *padding-top: 3px; + *padding-bottom: 3px; +} +button.btn.btn-mini, +input[type="submit"].btn.btn-mini { + *padding-top: 1px; + *padding-bottom: 1px; +} +.btn-link, +.btn-link:active, +.btn-link[disabled] { + background-color: transparent; + background-image: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.btn-link { + border-color: transparent; + cursor: pointer; + color: #0088cc; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.btn-link:hover, +.btn-link:focus { + color: #005580; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +.btn-link[disabled]:focus { + color: #333333; + text-decoration: none; +} +.btn-group { + position: relative; + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + font-size: 0; + vertical-align: middle; + white-space: nowrap; + *margin-left: .3em; +} +.btn-group:first-child { + *margin-left: 0; +} +.btn-group + .btn-group { + margin-left: 5px; +} +.btn-toolbar { + font-size: 0; + margin-top: 10px; + margin-bottom: 10px; +} +.btn-toolbar > .btn + .btn, +.btn-toolbar > .btn-group + .btn, +.btn-toolbar > .btn + .btn-group { + margin-left: 5px; +} +.btn-group > .btn { + position: relative; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.btn-group > .btn + .btn { + margin-left: -1px; +} +.btn-group > .btn, +.btn-group > .dropdown-menu, +.btn-group > .popover { + font-size: 14px; +} +.btn-group > .btn-mini { + font-size: 10.5px; +} +.btn-group > .btn-small { + font-size: 11.9px; +} +.btn-group > .btn-large { + font-size: 17.5px; +} +.btn-group > .btn:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; +} +.btn-group > .btn:last-child, +.btn-group > .dropdown-toggle { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; +} +.btn-group > .btn.large:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 6px; + -moz-border-radius-topleft: 6px; + border-top-left-radius: 6px; + -webkit-border-bottom-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + border-bottom-left-radius: 6px; +} +.btn-group > .btn.large:last-child, +.btn-group > .large.dropdown-toggle { + -webkit-border-top-right-radius: 6px; + -moz-border-radius-topright: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + -moz-border-radius-bottomright: 6px; + border-bottom-right-radius: 6px; +} +.btn-group > .btn:hover, +.btn-group > .btn:focus, +.btn-group > .btn:active, +.btn-group > .btn.active { + z-index: 2; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; + -webkit-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + *padding-top: 5px; + *padding-bottom: 5px; +} +.btn-group > .btn-mini + .dropdown-toggle { + padding-left: 5px; + padding-right: 5px; + *padding-top: 2px; + *padding-bottom: 2px; +} +.btn-group > .btn-small + .dropdown-toggle { + *padding-top: 5px; + *padding-bottom: 4px; +} +.btn-group > .btn-large + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; + *padding-top: 7px; + *padding-bottom: 7px; +} +.btn-group.open .dropdown-toggle { + background-image: none; + -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); + box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); +} +.btn-group.open .btn.dropdown-toggle { + background-color: #e6e6e6; +} +.btn-group.open .btn-primary.dropdown-toggle { + background-color: #0044cc; +} +.btn-group.open .btn-warning.dropdown-toggle { + background-color: #f89406; +} +.btn-group.open .btn-danger.dropdown-toggle { + background-color: #bd362f; +} +.btn-group.open .btn-success.dropdown-toggle { + background-color: #51a351; +} +.btn-group.open .btn-info.dropdown-toggle { + background-color: #2f96b4; +} +.btn-group.open .btn-inverse.dropdown-toggle { + background-color: #222222; +} +.btn .caret { + margin-top: 8px; + margin-left: 0; +} +.btn-large .caret { + margin-top: 6px; +} +.btn-large .caret { + border-left-width: 5px; + border-right-width: 5px; + border-top-width: 5px; +} +.btn-mini .caret, +.btn-small .caret { + margin-top: 8px; +} +.dropup .btn-large .caret { + border-bottom-width: 5px; +} +.btn-primary .caret, +.btn-warning .caret, +.btn-danger .caret, +.btn-info .caret, +.btn-success .caret, +.btn-inverse .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} +.btn-group-vertical { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; +} +.btn-group-vertical > .btn { + display: block; + float: none; + max-width: 100%; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.btn-group-vertical > .btn + .btn { + margin-left: 0; + margin-top: -1px; +} +.btn-group-vertical > .btn:first-child { + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} +.btn-group-vertical > .btn:last-child { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} +.btn-group-vertical > .btn-large:first-child { + -webkit-border-radius: 6px 6px 0 0; + -moz-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} +.btn-group-vertical > .btn-large:last-child { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} +.alert { + padding: 8px 35px 8px 14px; + margin-bottom: 20px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + background-color: #fcf8e3; + border: 1px solid #fbeed5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.alert, +.alert h4 { + color: #c09853; +} +.alert h4 { + margin: 0; +} +.alert .close { + position: relative; + top: -2px; + right: -21px; + line-height: 20px; +} +.alert-success { + background-color: #dff0d8; + border-color: #d6e9c6; + color: #468847; +} +.alert-success h4 { + color: #468847; +} +.alert-danger, +.alert-error { + background-color: #f2dede; + border-color: #eed3d7; + color: #b94a48; +} +.alert-danger h4, +.alert-error h4 { + color: #b94a48; +} +.alert-info { + background-color: #d9edf7; + border-color: #bce8f1; + color: #3a87ad; +} +.alert-info h4 { + color: #3a87ad; +} +.alert-block { + padding-top: 14px; + padding-bottom: 14px; +} +.alert-block > p, +.alert-block > ul { + margin-bottom: 0; +} +.alert-block p + p { + margin-top: 5px; +} +.nav { + margin-left: 0; + margin-bottom: 20px; + list-style: none; +} +.nav > li > a { + display: block; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.nav > li > a > img { + max-width: none; +} +.nav > .pull-right { + float: right; +} +.nav-header { + display: block; + padding: 3px 15px; + font-size: 11px; + font-weight: bold; + line-height: 20px; + color: #999999; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-transform: uppercase; +} +.nav li + .nav-header { + margin-top: 9px; +} +.nav-list { + padding-left: 15px; + padding-right: 15px; + margin-bottom: 0; +} +.nav-list > li > a, +.nav-list .nav-header { + margin-left: -15px; + margin-right: -15px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); +} +.nav-list > li > a { + padding: 3px 15px; +} +.nav-list > .active > a, +.nav-list > .active > a:hover, +.nav-list > .active > a:focus { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + background-color: #0088cc; +} +.nav-list [class^="icon-"], +.nav-list [class*=" icon-"] { + margin-right: 2px; +} +.nav-list .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} +.nav-tabs, +.nav-pills { + *zoom: 1; +} +.nav-tabs:before, +.nav-pills:before, +.nav-tabs:after, +.nav-pills:after { + display: table; + content: ""; + line-height: 0; +} +.nav-tabs:after, +.nav-pills:after { + clear: both; +} +.nav-tabs > li, +.nav-pills > li { + float: left; +} +.nav-tabs > li > a, +.nav-pills > li > a { + padding-right: 12px; + padding-left: 12px; + margin-right: 2px; + line-height: 14px; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + margin-bottom: -1px; +} +.nav-tabs > li > a { + padding-top: 8px; + padding-bottom: 8px; + line-height: 20px; + border: 1px solid transparent; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover, +.nav-tabs > li > a:focus { + border-color: #eeeeee #eeeeee #dddddd; +} +.nav-tabs > .active > a, +.nav-tabs > .active > a:hover, +.nav-tabs > .active > a:focus { + color: #555555; + background-color: #ffffff; + border: 1px solid #ddd; + border-bottom-color: transparent; + cursor: default; +} +.nav-pills > li > a { + padding-top: 8px; + padding-bottom: 8px; + margin-top: 2px; + margin-bottom: 2px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} +.nav-pills > .active > a, +.nav-pills > .active > a:hover, +.nav-pills > .active > a:focus { + color: #ffffff; + background-color: #0088cc; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li > a { + margin-right: 0; +} +.nav-tabs.nav-stacked { + border-bottom: 0; +} +.nav-tabs.nav-stacked > li > a { + border: 1px solid #ddd; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.nav-tabs.nav-stacked > li:first-child > a { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; +} +.nav-tabs.nav-stacked > li:last-child > a { + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; +} +.nav-tabs.nav-stacked > li > a:hover, +.nav-tabs.nav-stacked > li > a:focus { + border-color: #ddd; + z-index: 2; +} +.nav-pills.nav-stacked > li > a { + margin-bottom: 3px; +} +.nav-pills.nav-stacked > li:last-child > a { + margin-bottom: 1px; +} +.nav-tabs .dropdown-menu { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} +.nav-pills .dropdown-menu { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.nav .dropdown-toggle .caret { + border-top-color: #0088cc; + border-bottom-color: #0088cc; + margin-top: 6px; +} +.nav .dropdown-toggle:hover .caret, +.nav .dropdown-toggle:focus .caret { + border-top-color: #005580; + border-bottom-color: #005580; +} +/* move down carets for tabs */ +.nav-tabs .dropdown-toggle .caret { + margin-top: 8px; +} +.nav .active .dropdown-toggle .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} +.nav-tabs .active .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} +.nav > .dropdown.active > a:hover, +.nav > .dropdown.active > a:focus { + cursor: pointer; +} +.nav-tabs .open .dropdown-toggle, +.nav-pills .open .dropdown-toggle, +.nav > li.dropdown.open.active > a:hover, +.nav > li.dropdown.open.active > a:focus { + color: #ffffff; + background-color: #999999; + border-color: #999999; +} +.nav li.dropdown.open .caret, +.nav li.dropdown.open.active .caret, +.nav li.dropdown.open a:hover .caret, +.nav li.dropdown.open a:focus .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; + opacity: 1; + filter: alpha(opacity=100); +} +.tabs-stacked .open > a:hover, +.tabs-stacked .open > a:focus { + border-color: #999999; +} +.tabbable { + *zoom: 1; +} +.tabbable:before, +.tabbable:after { + display: table; + content: ""; + line-height: 0; +} +.tabbable:after { + clear: both; +} +.tab-content { + overflow: auto; +} +.tabs-below > .nav-tabs, +.tabs-right > .nav-tabs, +.tabs-left > .nav-tabs { + border-bottom: 0; +} +.tab-content > .tab-pane, +.pill-content > .pill-pane { + display: none; +} +.tab-content > .active, +.pill-content > .active { + display: block; +} +.tabs-below > .nav-tabs { + border-top: 1px solid #ddd; +} +.tabs-below > .nav-tabs > li { + margin-top: -1px; + margin-bottom: 0; +} +.tabs-below > .nav-tabs > li > a { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} +.tabs-below > .nav-tabs > li > a:hover, +.tabs-below > .nav-tabs > li > a:focus { + border-bottom-color: transparent; + border-top-color: #ddd; +} +.tabs-below > .nav-tabs > .active > a, +.tabs-below > .nav-tabs > .active > a:hover, +.tabs-below > .nav-tabs > .active > a:focus { + border-color: transparent #ddd #ddd #ddd; +} +.tabs-left > .nav-tabs > li, +.tabs-right > .nav-tabs > li { + float: none; +} +.tabs-left > .nav-tabs > li > a, +.tabs-right > .nav-tabs > li > a { + min-width: 74px; + margin-right: 0; + margin-bottom: 3px; +} +.tabs-left > .nav-tabs { + float: left; + margin-right: 19px; + border-right: 1px solid #ddd; +} +.tabs-left > .nav-tabs > li > a { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +.tabs-left > .nav-tabs > li > a:hover, +.tabs-left > .nav-tabs > li > a:focus { + border-color: #eeeeee #dddddd #eeeeee #eeeeee; +} +.tabs-left > .nav-tabs .active > a, +.tabs-left > .nav-tabs .active > a:hover, +.tabs-left > .nav-tabs .active > a:focus { + border-color: #ddd transparent #ddd #ddd; + *border-right-color: #ffffff; +} +.tabs-right > .nav-tabs { + float: right; + margin-left: 19px; + border-left: 1px solid #ddd; +} +.tabs-right > .nav-tabs > li > a { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.tabs-right > .nav-tabs > li > a:hover, +.tabs-right > .nav-tabs > li > a:focus { + border-color: #eeeeee #eeeeee #eeeeee #dddddd; +} +.tabs-right > .nav-tabs .active > a, +.tabs-right > .nav-tabs .active > a:hover, +.tabs-right > .nav-tabs .active > a:focus { + border-color: #ddd #ddd #ddd transparent; + *border-left-color: #ffffff; +} +.nav > .disabled > a { + color: #999999; +} +.nav > .disabled > a:hover, +.nav > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + cursor: default; +} +.navbar { + overflow: visible; + margin-bottom: 20px; + *position: relative; + *z-index: 2; +} +.navbar-inner { + min-height: 40px; + padding-left: 20px; + padding-right: 20px; + background-color: #fafafa; + background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2)); + background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -o-linear-gradient(top, #ffffff, #f2f2f2); + background-image: linear-gradient(to bottom, #ffffff, #f2f2f2); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0); + border: 1px solid #d4d4d4; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + *zoom: 1; +} +.navbar-inner:before, +.navbar-inner:after { + display: table; + content: ""; + line-height: 0; +} +.navbar-inner:after { + clear: both; +} +.navbar .container { + width: auto; +} +.nav-collapse.collapse { + height: auto; + overflow: visible; +} +.navbar .brand { + float: left; + display: block; + padding: 10px 20px 10px; + margin-left: -20px; + font-size: 20px; + font-weight: 200; + color: #777777; + text-shadow: 0 1px 0 #ffffff; +} +.navbar .brand:hover, +.navbar .brand:focus { + text-decoration: none; +} +.navbar-text { + margin-bottom: 0; + line-height: 40px; + color: #777777; +} +.navbar-link { + color: #777777; +} +.navbar-link:hover, +.navbar-link:focus { + color: #333333; +} +.navbar .divider-vertical { + height: 40px; + margin: 0 9px; + border-left: 1px solid #f2f2f2; + border-right: 1px solid #ffffff; +} +.navbar .btn, +.navbar .btn-group { + margin-top: 5px; +} +.navbar .btn-group .btn, +.navbar .input-prepend .btn, +.navbar .input-append .btn, +.navbar .input-prepend .btn-group, +.navbar .input-append .btn-group { + margin-top: 0; +} +.navbar-form { + margin-bottom: 0; + *zoom: 1; +} +.navbar-form:before, +.navbar-form:after { + display: table; + content: ""; + line-height: 0; +} +.navbar-form:after { + clear: both; +} +.navbar-form input, +.navbar-form select, +.navbar-form .radio, +.navbar-form .checkbox { + margin-top: 5px; +} +.navbar-form input, +.navbar-form select, +.navbar-form .btn { + display: inline-block; + margin-bottom: 0; +} +.navbar-form input[type="image"], +.navbar-form input[type="checkbox"], +.navbar-form input[type="radio"] { + margin-top: 3px; +} +.navbar-form .input-append, +.navbar-form .input-prepend { + margin-top: 5px; + white-space: nowrap; +} +.navbar-form .input-append input, +.navbar-form .input-prepend input { + margin-top: 0; +} +.navbar-search { + position: relative; + float: left; + margin-top: 5px; + margin-bottom: 0; +} +.navbar-search .search-query { + margin-bottom: 0; + padding: 4px 14px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + font-weight: normal; + line-height: 1; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} +.navbar-static-top { + position: static; + margin-bottom: 0; +} +.navbar-static-top .navbar-inner { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + margin-bottom: 0; +} +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + border-width: 0 0 1px; +} +.navbar-fixed-bottom .navbar-inner { + border-width: 1px 0 0; +} +.navbar-fixed-top .navbar-inner, +.navbar-fixed-bottom .navbar-inner { + padding-left: 0; + padding-right: 0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} +.navbar-fixed-top { + top: 0; +} +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + -webkit-box-shadow: 0 1px 10px rgba(0,0,0,.1); + -moz-box-shadow: 0 1px 10px rgba(0,0,0,.1); + box-shadow: 0 1px 10px rgba(0,0,0,.1); +} +.navbar-fixed-bottom { + bottom: 0; +} +.navbar-fixed-bottom .navbar-inner { + -webkit-box-shadow: 0 -1px 10px rgba(0,0,0,.1); + -moz-box-shadow: 0 -1px 10px rgba(0,0,0,.1); + box-shadow: 0 -1px 10px rgba(0,0,0,.1); +} +.navbar .nav { + position: relative; + left: 0; + display: block; + float: left; + margin: 0 10px 0 0; +} +.navbar .nav.pull-right { + float: right; + margin-right: 0; +} +.navbar .nav > li { + float: left; +} +.navbar .nav > li > a { + float: none; + padding: 10px 15px 10px; + color: #777777; + text-decoration: none; + text-shadow: 0 1px 0 #ffffff; +} +.navbar .nav .dropdown-toggle .caret { + margin-top: 8px; +} +.navbar .nav > li > a:focus, +.navbar .nav > li > a:hover { + background-color: transparent; + color: #333333; + text-decoration: none; +} +.navbar .nav > .active > a, +.navbar .nav > .active > a:hover, +.navbar .nav > .active > a:focus { + color: #555555; + text-decoration: none; + background-color: #e5e5e5; + -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); +} +.navbar .btn-navbar { + display: none; + float: right; + padding: 7px 10px; + margin-left: 5px; + margin-right: 5px; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #ededed; + background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5)); + background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0); + border-color: #e5e5e5 #e5e5e5 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #e5e5e5; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); + -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); + box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); +} +.navbar .btn-navbar:hover, +.navbar .btn-navbar:focus, +.navbar .btn-navbar:active, +.navbar .btn-navbar.active, +.navbar .btn-navbar.disabled, +.navbar .btn-navbar[disabled] { + color: #ffffff; + background-color: #e5e5e5; + *background-color: #d9d9d9; +} +.navbar .btn-navbar:active, +.navbar .btn-navbar.active { + background-color: #cccccc \9; +} +.navbar .btn-navbar .icon-bar { + display: block; + width: 18px; + height: 2px; + background-color: #f5f5f5; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); +} +.btn-navbar .icon-bar + .icon-bar { + margin-top: 3px; +} +.navbar .nav > li > .dropdown-menu:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + position: absolute; + top: -7px; + left: 9px; +} +.navbar .nav > li > .dropdown-menu:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + position: absolute; + top: -6px; + left: 10px; +} +.navbar-fixed-bottom .nav > li > .dropdown-menu:before { + border-top: 7px solid #ccc; + border-top-color: rgba(0, 0, 0, 0.2); + border-bottom: 0; + bottom: -7px; + top: auto; +} +.navbar-fixed-bottom .nav > li > .dropdown-menu:after { + border-top: 6px solid #ffffff; + border-bottom: 0; + bottom: -6px; + top: auto; +} +.navbar .nav li.dropdown > a:hover .caret, +.navbar .nav li.dropdown > a:focus .caret { + border-top-color: #333333; + border-bottom-color: #333333; +} +.navbar .nav li.dropdown.open > .dropdown-toggle, +.navbar .nav li.dropdown.active > .dropdown-toggle, +.navbar .nav li.dropdown.open.active > .dropdown-toggle { + background-color: #e5e5e5; + color: #555555; +} +.navbar .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #777777; + border-bottom-color: #777777; +} +.navbar .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} +.navbar .pull-right > li > .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right { + left: auto; + right: 0; +} +.navbar .pull-right > li > .dropdown-menu:before, +.navbar .nav > li > .dropdown-menu.pull-right:before { + left: auto; + right: 12px; +} +.navbar .pull-right > li > .dropdown-menu:after, +.navbar .nav > li > .dropdown-menu.pull-right:after { + left: auto; + right: 13px; +} +.navbar .pull-right > li > .dropdown-menu .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu { + left: auto; + right: 100%; + margin-left: 0; + margin-right: -1px; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} +.navbar-inverse .navbar-inner { + background-color: #1b1b1b; + background-image: -moz-linear-gradient(top, #222222, #111111); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111)); + background-image: -webkit-linear-gradient(top, #222222, #111111); + background-image: -o-linear-gradient(top, #222222, #111111); + background-image: linear-gradient(to bottom, #222222, #111111); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0); + border-color: #252525; +} +.navbar-inverse .brand, +.navbar-inverse .nav > li > a { + color: #999999; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.navbar-inverse .brand:hover, +.navbar-inverse .nav > li > a:hover, +.navbar-inverse .brand:focus, +.navbar-inverse .nav > li > a:focus { + color: #ffffff; +} +.navbar-inverse .brand { + color: #999999; +} +.navbar-inverse .navbar-text { + color: #999999; +} +.navbar-inverse .nav > li > a:focus, +.navbar-inverse .nav > li > a:hover { + background-color: transparent; + color: #ffffff; +} +.navbar-inverse .nav .active > a, +.navbar-inverse .nav .active > a:hover, +.navbar-inverse .nav .active > a:focus { + color: #ffffff; + background-color: #111111; +} +.navbar-inverse .navbar-link { + color: #999999; +} +.navbar-inverse .navbar-link:hover, +.navbar-inverse .navbar-link:focus { + color: #ffffff; +} +.navbar-inverse .divider-vertical { + border-left-color: #111111; + border-right-color: #222222; +} +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle { + background-color: #111111; + color: #ffffff; +} +.navbar-inverse .nav li.dropdown > a:hover .caret, +.navbar-inverse .nav li.dropdown > a:focus .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} +.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #999999; + border-bottom-color: #999999; +} +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} +.navbar-inverse .navbar-search .search-query { + color: #ffffff; + background-color: #515151; + border-color: #111111; + -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15); + -moz-box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15); + box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15); + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; +} +.navbar-inverse .navbar-search .search-query:-moz-placeholder { + color: #cccccc; +} +.navbar-inverse .navbar-search .search-query:-ms-input-placeholder { + color: #cccccc; +} +.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder { + color: #cccccc; +} +.navbar-inverse .navbar-search .search-query:focus, +.navbar-inverse .navbar-search .search-query.focused { + padding: 5px 15px; + color: #333333; + text-shadow: 0 1px 0 #ffffff; + background-color: #ffffff; + border: 0; + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + outline: 0; +} +.navbar-inverse .btn-navbar { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e0e0e; + background-image: -moz-linear-gradient(top, #151515, #040404); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404)); + background-image: -webkit-linear-gradient(top, #151515, #040404); + background-image: -o-linear-gradient(top, #151515, #040404); + background-image: linear-gradient(to bottom, #151515, #040404); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0); + border-color: #040404 #040404 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #040404; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.navbar-inverse .btn-navbar:hover, +.navbar-inverse .btn-navbar:focus, +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active, +.navbar-inverse .btn-navbar.disabled, +.navbar-inverse .btn-navbar[disabled] { + color: #ffffff; + background-color: #040404; + *background-color: #000000; +} +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active { + background-color: #000000 \9; +} +.breadcrumb { + padding: 8px 15px; + margin: 0 0 20px; + list-style: none; + background-color: #f5f5f5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + text-shadow: 0 1px 0 #ffffff; +} +.breadcrumb > li > .divider { + padding: 0 5px; + color: #ccc; +} +.breadcrumb > .active { + color: #999999; +} +.pagination { + margin: 20px 0; +} +.pagination ul { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + margin-left: 0; + margin-bottom: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} +.pagination ul > li { + display: inline; +} +.pagination ul > li > a, +.pagination ul > li > span { + float: left; + padding: 4px 12px; + line-height: 20px; + text-decoration: none; + background-color: #ffffff; + border: 1px solid #dddddd; + border-left-width: 0; +} +.pagination ul > li > a:hover, +.pagination ul > li > a:focus, +.pagination ul > .active > a, +.pagination ul > .active > span { + background-color: #f5f5f5; +} +.pagination ul > .active > a, +.pagination ul > .active > span { + color: #999999; + cursor: default; +} +.pagination ul > .disabled > span, +.pagination ul > .disabled > a, +.pagination ul > .disabled > a:hover, +.pagination ul > .disabled > a:focus { + color: #999999; + background-color: transparent; + cursor: default; +} +.pagination ul > li:first-child > a, +.pagination ul > li:first-child > span { + border-left-width: 1px; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; +} +.pagination ul > li:last-child > a, +.pagination ul > li:last-child > span { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; +} +.pagination-centered { + text-align: center; +} +.pagination-right { + text-align: right; +} +.pagination-large ul > li > a, +.pagination-large ul > li > span { + padding: 11px 19px; + font-size: 17.5px; +} +.pagination-large ul > li:first-child > a, +.pagination-large ul > li:first-child > span { + -webkit-border-top-left-radius: 6px; + -moz-border-radius-topleft: 6px; + border-top-left-radius: 6px; + -webkit-border-bottom-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + border-bottom-left-radius: 6px; +} +.pagination-large ul > li:last-child > a, +.pagination-large ul > li:last-child > span { + -webkit-border-top-right-radius: 6px; + -moz-border-radius-topright: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + -moz-border-radius-bottomright: 6px; + border-bottom-right-radius: 6px; +} +.pagination-mini ul > li:first-child > a, +.pagination-small ul > li:first-child > a, +.pagination-mini ul > li:first-child > span, +.pagination-small ul > li:first-child > span { + -webkit-border-top-left-radius: 3px; + -moz-border-radius-topleft: 3px; + border-top-left-radius: 3px; + -webkit-border-bottom-left-radius: 3px; + -moz-border-radius-bottomleft: 3px; + border-bottom-left-radius: 3px; +} +.pagination-mini ul > li:last-child > a, +.pagination-small ul > li:last-child > a, +.pagination-mini ul > li:last-child > span, +.pagination-small ul > li:last-child > span { + -webkit-border-top-right-radius: 3px; + -moz-border-radius-topright: 3px; + border-top-right-radius: 3px; + -webkit-border-bottom-right-radius: 3px; + -moz-border-radius-bottomright: 3px; + border-bottom-right-radius: 3px; +} +.pagination-small ul > li > a, +.pagination-small ul > li > span { + padding: 2px 10px; + font-size: 11.9px; +} +.pagination-mini ul > li > a, +.pagination-mini ul > li > span { + padding: 0 6px; + font-size: 10.5px; +} +.pager { + margin: 20px 0; + list-style: none; + text-align: center; + *zoom: 1; +} +.pager:before, +.pager:after { + display: table; + content: ""; + line-height: 0; +} +.pager:after { + clear: both; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #f5f5f5; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + background-color: #fff; + cursor: default; +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000000; +} +.modal-backdrop.fade { + opacity: 0; +} +.modal-backdrop, +.modal-backdrop.fade.in { + opacity: 0.8; + filter: alpha(opacity=80); +} +.modal { + position: fixed; + top: 10%; + left: 50%; + z-index: 1050; + width: 560px; + margin-left: -280px; + background-color: #ffffff; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, 0.3); + *border: 1px solid #999; + /* IE6-7 */ + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; + outline: none; +} +.modal.fade { + -webkit-transition: opacity .3s linear, top .3s ease-out; + -moz-transition: opacity .3s linear, top .3s ease-out; + -o-transition: opacity .3s linear, top .3s ease-out; + transition: opacity .3s linear, top .3s ease-out; + top: -25%; +} +.modal.fade.in { + top: 10%; +} +.modal-header { + padding: 9px 15px; + border-bottom: 1px solid #eee; +} +.modal-header .close { + margin-top: 2px; +} +.modal-header h3 { + margin: 0; + line-height: 30px; +} +.modal-body { + position: relative; + overflow-y: auto; + max-height: 400px; + padding: 15px; +} +.modal-form { + margin-bottom: 0; +} +.modal-footer { + padding: 14px 15px 15px; + margin-bottom: 0; + text-align: right; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; + *zoom: 1; +} +.modal-footer:before, +.modal-footer:after { + display: table; + content: ""; + line-height: 0; +} +.modal-footer:after { + clear: both; +} +.modal-footer .btn + .btn { + margin-left: 5px; + margin-bottom: 0; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.tooltip { + position: absolute; + z-index: 1030; + display: block; + visibility: visible; + font-size: 11px; + line-height: 1.4; + opacity: 0; + filter: alpha(opacity=0); +} +.tooltip.in { + opacity: 0.8; + filter: alpha(opacity=80); +} +.tooltip.top { + margin-top: -3px; + padding: 5px 0; +} +.tooltip.right { + margin-left: 3px; + padding: 0 5px; +} +.tooltip.bottom { + margin-top: 3px; + padding: 5px 0; +} +.tooltip.left { + margin-left: -3px; + padding: 0 5px; +} +.tooltip-inner { + max-width: 200px; + padding: 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + max-width: 276px; + padding: 1px; + text-align: left; + background-color: #ffffff; + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + white-space: normal; +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + margin: 0; + padding: 8px 14px; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + -webkit-border-radius: 5px 5px 0 0; + -moz-border-radius: 5px 5px 0 0; + border-radius: 5px 5px 0 0; +} +.popover-title:empty { + display: none; +} +.popover-content { + padding: 9px 14px; +} +.popover .arrow, +.popover .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover .arrow { + border-width: 11px; +} +.popover .arrow:after { + border-width: 10px; + content: ""; +} +.popover.top .arrow { + left: 50%; + margin-left: -11px; + border-bottom-width: 0; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, 0.25); + bottom: -11px; +} +.popover.top .arrow:after { + bottom: 1px; + margin-left: -10px; + border-bottom-width: 0; + border-top-color: #ffffff; +} +.popover.right .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-left-width: 0; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, 0.25); +} +.popover.right .arrow:after { + left: 1px; + bottom: -10px; + border-left-width: 0; + border-right-color: #ffffff; +} +.popover.bottom .arrow { + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, 0.25); + top: -11px; +} +.popover.bottom .arrow:after { + top: 1px; + margin-left: -10px; + border-top-width: 0; + border-bottom-color: #ffffff; +} +.popover.left .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, 0.25); +} +.popover.left .arrow:after { + right: 1px; + border-right-width: 0; + border-left-color: #ffffff; + bottom: -10px; +} +.thumbnails { + margin-left: -20px; + list-style: none; + *zoom: 1; +} +.thumbnails:before, +.thumbnails:after { + display: table; + content: ""; + line-height: 0; +} +.thumbnails:after { + clear: both; +} +.row-fluid .thumbnails { + margin-left: 0; +} +.thumbnails > li { + float: left; + margin-bottom: 20px; + margin-left: 20px; +} +.thumbnail { + display: block; + padding: 4px; + line-height: 20px; + border: 1px solid #ddd; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} +a.thumbnail:hover, +a.thumbnail:focus { + border-color: #0088cc; + -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); +} +.thumbnail > img { + display: block; + max-width: 100%; + margin-left: auto; + margin-right: auto; +} +.thumbnail .caption { + padding: 9px; + color: #555555; +} +.media, +.media-body { + overflow: hidden; + *overflow: visible; + zoom: 1; +} +.media, +.media .media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media-object { + display: block; +} +.media-heading { + margin: 0 0 5px; +} +.media > .pull-left { + margin-right: 10px; +} +.media > .pull-right { + margin-left: 10px; +} +.media-list { + margin-left: 0; + list-style: none; +} +.label, +.badge { + display: inline-block; + padding: 2px 4px; + font-size: 11.844px; + font-weight: bold; + line-height: 14px; + color: #ffffff; + vertical-align: baseline; + white-space: nowrap; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #999999; +} +.label { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.badge { + padding-left: 9px; + padding-right: 9px; + -webkit-border-radius: 9px; + -moz-border-radius: 9px; + border-radius: 9px; +} +.label:empty, +.badge:empty { + display: none; +} +a.label:hover, +a.label:focus, +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +.label-important, +.badge-important { + background-color: #b94a48; +} +.label-important[href], +.badge-important[href] { + background-color: #953b39; +} +.label-warning, +.badge-warning { + background-color: #f89406; +} +.label-warning[href], +.badge-warning[href] { + background-color: #c67605; +} +.label-success, +.badge-success { + background-color: #468847; +} +.label-success[href], +.badge-success[href] { + background-color: #356635; +} +.label-info, +.badge-info { + background-color: #3a87ad; +} +.label-info[href], +.badge-info[href] { + background-color: #2d6987; +} +.label-inverse, +.badge-inverse { + background-color: #333333; +} +.label-inverse[href], +.badge-inverse[href] { + background-color: #1a1a1a; +} +.btn .label, +.btn .badge { + position: relative; + top: -1px; +} +.btn-mini .label, +.btn-mini .badge { + top: 0; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-moz-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-ms-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + overflow: hidden; + height: 20px; + margin-bottom: 20px; + background-color: #f7f7f7; + background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); + background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.progress .bar { + width: 0%; + height: 100%; + color: #ffffff; + float: left; + font-size: 12px; + text-align: center; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e90d2; + background-image: -moz-linear-gradient(top, #149bdf, #0480be); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); + background-image: -webkit-linear-gradient(top, #149bdf, #0480be); + background-image: -o-linear-gradient(top, #149bdf, #0480be); + background-image: linear-gradient(to bottom, #149bdf, #0480be); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: width 0.6s ease; + -moz-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} +.progress .bar + .bar { + -webkit-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); + -moz-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); + box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); +} +.progress-striped .bar { + background-color: #149bdf; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + -moz-background-size: 40px 40px; + -o-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + -ms-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-danger .bar, +.progress .bar-danger { + background-color: #dd514c; + background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); + background-image: linear-gradient(to bottom, #ee5f5b, #c43c35); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0); +} +.progress-danger.progress-striped .bar, +.progress-striped .bar-danger { + background-color: #ee5f5b; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-success .bar, +.progress .bar-success { + background-color: #5eb95e; + background-image: -moz-linear-gradient(top, #62c462, #57a957); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); + background-image: -webkit-linear-gradient(top, #62c462, #57a957); + background-image: -o-linear-gradient(top, #62c462, #57a957); + background-image: linear-gradient(to bottom, #62c462, #57a957); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0); +} +.progress-success.progress-striped .bar, +.progress-striped .bar-success { + background-color: #62c462; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-info .bar, +.progress .bar-info { + background-color: #4bb1cf; + background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); + background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); + background-image: -o-linear-gradient(top, #5bc0de, #339bb9); + background-image: linear-gradient(to bottom, #5bc0de, #339bb9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0); +} +.progress-info.progress-striped .bar, +.progress-striped .bar-info { + background-color: #5bc0de; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-warning .bar, +.progress .bar-warning { + background-color: #faa732; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); +} +.progress-warning.progress-striped .bar, +.progress-striped .bar-warning { + background-color: #fbb450; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.accordion { + margin-bottom: 20px; +} +.accordion-group { + margin-bottom: 2px; + border: 1px solid #e5e5e5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.accordion-heading { + border-bottom: 0; +} +.accordion-heading .accordion-toggle { + display: block; + padding: 8px 15px; +} +.accordion-toggle { + cursor: pointer; +} +.accordion-inner { + padding: 9px 15px; + border-top: 1px solid #e5e5e5; +} +.carousel { + position: relative; + margin-bottom: 20px; + line-height: 1; +} +.carousel-inner { + overflow: hidden; + width: 100%; + position: relative; +} +.carousel-inner > .item { + display: none; + position: relative; + -webkit-transition: 0.6s ease-in-out left; + -moz-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + line-height: 1; +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 40%; + left: 15px; + width: 40px; + height: 40px; + margin-top: -20px; + font-size: 60px; + font-weight: 100; + line-height: 30px; + color: #ffffff; + text-align: center; + background: #222222; + border: 3px solid #ffffff; + -webkit-border-radius: 23px; + -moz-border-radius: 23px; + border-radius: 23px; + opacity: 0.5; + filter: alpha(opacity=50); +} +.carousel-control.right { + left: auto; + right: 15px; +} +.carousel-control:hover, +.carousel-control:focus { + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} +.carousel-indicators { + position: absolute; + top: 15px; + right: 15px; + z-index: 5; + margin: 0; + list-style: none; +} +.carousel-indicators li { + display: block; + float: left; + width: 10px; + height: 10px; + margin-left: 5px; + text-indent: -999px; + background-color: #ccc; + background-color: rgba(255, 255, 255, 0.25); + border-radius: 5px; +} +.carousel-indicators .active { + background-color: #fff; +} +.carousel-caption { + position: absolute; + left: 0; + right: 0; + bottom: 0; + padding: 15px; + background: #333333; + background: rgba(0, 0, 0, 0.75); +} +.carousel-caption h4, +.carousel-caption p { + color: #ffffff; + line-height: 20px; +} +.carousel-caption h4 { + margin: 0 0 5px; +} +.carousel-caption p { + margin-bottom: 0; +} +.hero-unit { + padding: 60px; + margin-bottom: 30px; + font-size: 18px; + font-weight: 200; + line-height: 30px; + color: inherit; + background-color: #eeeeee; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.hero-unit h1 { + margin-bottom: 0; + font-size: 60px; + line-height: 1; + color: inherit; + letter-spacing: -1px; +} +.hero-unit li { + line-height: 30px; +} +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.hide { + display: none; +} +.show { + display: block; +} +.invisible { + visibility: hidden; +} +.affix { + position: fixed; +} + diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/exception/DeviceCodeExpiredException.java b/openid-connect-server/src/main/java/org/mitre/oauth2/exception/DeviceCodeExpiredException.java new file mode 100644 index 0000000000..6444f184a4 --- /dev/null +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/exception/DeviceCodeExpiredException.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.oauth2.exception; + +import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; + +/** + * @author jricher + * + */ +public class DeviceCodeExpiredException extends OAuth2Exception { + + /** + * @param msg + */ + public DeviceCodeExpiredException(String msg) { + super(msg); + } + + /** + * + */ + private static final long serialVersionUID = -7078098692596870940L; + + /* (non-Javadoc) + * @see org.springframework.security.oauth2.common.exceptions.OAuth2Exception#getOAuth2ErrorCode() + */ + @Override + public String getOAuth2ErrorCode() { + return "expired_token"; + } + +} diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/InMemoryDeviceCodeService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/InMemoryDeviceCodeService.java new file mode 100644 index 0000000000..eb522c5b69 --- /dev/null +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/InMemoryDeviceCodeService.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.oauth2.service.impl; + +import java.util.Date; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.mitre.oauth2.model.AuthenticationHolderEntity; +import org.mitre.oauth2.model.ClientDetailsEntity; +import org.mitre.oauth2.model.DeviceCode; +import org.mitre.oauth2.service.DeviceCodeService; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.provider.ClientDetails; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.stereotype.Service; + +/** + * @author jricher + * + */ +@Service +public class InMemoryDeviceCodeService implements DeviceCodeService { + + private Set codes = new HashSet<>(); + + /* (non-Javadoc) + * @see org.mitre.oauth2.service.DeviceCodeService#save(org.mitre.oauth2.model.DeviceCode) + */ + @Override + public DeviceCode createNewDeviceCode(String deviceCode, String userCode, Set requestedScopes, ClientDetailsEntity client, Map parameters) { + + DeviceCode dc = new DeviceCode(deviceCode, userCode, requestedScopes, client.getClientId(), parameters); + + if (client.getDeviceCodeValiditySeconds() != null) { + dc.setExpiration(new Date(System.currentTimeMillis() + client.getDeviceCodeValiditySeconds() * 1000L)); + } + + dc.setApproved(false); + + codes.add(dc); + return dc; + } + + /* (non-Javadoc) + * @see org.mitre.oauth2.service.DeviceCodeService#lookUpByUserCode(java.lang.String) + */ + @Override + public DeviceCode lookUpByUserCode(String userCode) { + for (DeviceCode dc : codes) { + if (dc.getUserCode().equals(userCode)) { + return dc; + } + } + return null; + } + + /* (non-Javadoc) + * @see org.mitre.oauth2.service.DeviceCodeService#approveDeviceCode(org.mitre.oauth2.model.DeviceCode) + */ + @Override + public DeviceCode approveDeviceCode(DeviceCode dc, OAuth2Authentication auth) { + dc.setApproved(true); + + AuthenticationHolderEntity authHolder = new AuthenticationHolderEntity(); + authHolder.setAuthentication(auth); + dc.setAuthenticationHolder(authHolder); + + return dc; + } + + /* (non-Javadoc) + * @see org.mitre.oauth2.service.DeviceCodeService#consumeDeviceCode(java.lang.String, org.springframework.security.oauth2.provider.ClientDetails) + */ + @Override + public DeviceCode consumeDeviceCode(String deviceCode, ClientDetails client) { + for (DeviceCode dc : codes) { + if (dc.getDeviceCode().equals(deviceCode) && dc.getClientId().equals(client.getClientId())) { + codes.remove(dc); + return dc; + } + } + return null; + } + +} diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java b/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java index f8252cab7a..7a47aa19ba 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java @@ -17,7 +17,10 @@ package org.mitre.oauth2.token; +import java.util.Date; + import org.mitre.oauth2.exception.AuthorizationPendingException; +import org.mitre.oauth2.exception.DeviceCodeExpiredException; import org.mitre.oauth2.model.DeviceCode; import org.mitre.oauth2.service.DeviceCodeService; import org.mitre.oauth2.web.DeviceEndpoint; @@ -71,18 +74,23 @@ protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, Tok if (dc != null) { - if (dc.isApproved()) { + // make sure the code hasn't expired yet + if (dc.getExpiration() != null && dc.getExpiration().before(new Date())) { + // TODO: return an error + throw new DeviceCodeExpiredException("Device code has expired " + deviceCode); + + } else if (!dc.isApproved()) { + + // still waiting for approval + throw new AuthorizationPendingException("Authorization pending for code " + deviceCode); + } else { // inherit the (approved) scopes from the original request tokenRequest.setScope(dc.getScope()); OAuth2Authentication auth = new OAuth2Authentication(getRequestFactory().createOAuth2Request(client, tokenRequest), dc.getAuthenticationHolder().getUserAuth()); return auth; - } else { - - // still waiting for approval - throw new AuthorizationPendingException("Authorization pending for code " + deviceCode); } } else { throw new InvalidGrantException("Invalid device code: " + deviceCode); diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java index 975f0b0737..31028ef447 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java @@ -18,6 +18,7 @@ package org.mitre.oauth2.web; import java.util.Collection; +import java.util.Date; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; @@ -144,17 +145,15 @@ public String requestDeviceCode(@RequestParam("client_id") String clientId, @Req // create a user code, should be random but small and typable String userCode = randomGenerator.generate(); - // TODO: expiration + DeviceCode dc = deviceCodeService.createNewDeviceCode(deviceCode, userCode, requestedScopes, client, parameters); + model.put(JsonEntityView.ENTITY, ImmutableMap.of( "device_code", deviceCode, "user_code", userCode, - "verification_uri", config.getIssuer() + URL + "verification_uri", config.getIssuer() + URL, + "expires_in", client.getDeviceCodeValiditySeconds() )); - DeviceCode dc = new DeviceCode(deviceCode, userCode, requestedScopes, clientId, parameters); - - - deviceCodeService.save(dc); return JsonEntityView.VIEWNAME; @@ -177,6 +176,24 @@ public String readUserCode(@RequestParam("user_code") String userCode, ModelMap // look up the request based on the user code DeviceCode dc = deviceCodeService.lookUpByUserCode(userCode); + // we couldn't find the device code + if (dc == null) { + // TODO: return error + return "error"; + } + + // make sure the code hasn't expired yet + if (dc.getExpiration() != null && dc.getExpiration().before(new Date())) { + // TODO: return an error + return "error"; + } + + // make sure the device code hasn't already been approved + if (dc.isApproved()) { + // TODO: return an error + return "error"; + } + ClientDetailsEntity client = clientService.loadClientByClientId(dc.getClientId()); model.put("client", client); @@ -210,16 +227,30 @@ public String readUserCode(@RequestParam("user_code") String userCode, ModelMap @PreAuthorize("hasRole('ROLE_USER')") @RequestMapping(value = "/" + USER_URL + "/approve", method = RequestMethod.POST) - public String approveDevice(@RequestParam("user_code") String userCode, @RequestParam(value = "user_oauth_approval") String approve, ModelMap model, Authentication auth, HttpSession session) { + public String approveDevice(@RequestParam("user_code") String userCode, @RequestParam(value = "user_oauth_approval") Boolean approve, ModelMap model, Authentication auth, HttpSession session) { AuthorizationRequest authorizationRequest = (AuthorizationRequest) session.getAttribute("authorizationRequest"); DeviceCode dc = (DeviceCode) session.getAttribute("deviceCode"); + // make sure the form that was submitted is the one that we were expecting if (!dc.getUserCode().equals(userCode)) { // TODO: return an error return "error"; } + // make sure the code hasn't expired yet + if (dc.getExpiration() != null && dc.getExpiration().before(new Date())) { + // TODO: return an error + return "error"; + } + + // user did not approve + if (!approve) { + // TODO: return an error + return "error"; + } + + // create an OAuth request for storage OAuth2Request o2req = oAuth2RequestFactory.createOAuth2Request(authorizationRequest); OAuth2Authentication o2Auth = new OAuth2Authentication(o2req, auth); From 04dd67d073f5f13d1b1297118d9feb72f145e3b1 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sat, 11 Mar 2017 15:29:24 -0500 Subject: [PATCH 147/269] added device codes to database --- .../db/hsql/hsql_database_tables.sql | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql index bf8e827f91..8f5e69ce7c 100644 --- a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql @@ -360,6 +360,26 @@ CREATE TABLE IF NOT EXISTS saved_registered_client ( registered_client VARCHAR(8192) ); +CREATE TABLE IF NOT EXISTS device_code ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, + device_code VARCHAR(1024), + user_code VARCHAR(1024), + expiration TIMESTAMP, + client_id VARCHAR(256), + approved BOOLEAN, + auth_holder_id BIGINT +); + +CREATE TABLE IF NOT EXISTS device_code_scope ( + owner_id BIGINT NOT NULL, + scope VARCHAR(256) NOT NULL +); + +CREATE TABLE IF NOT EXISTS device_code_request_parameter ( + owner_id BIGINT, + param VARCHAR(2048), + val VARCHAR(2048) +); CREATE INDEX at_tv_idx ON access_token(token_value); CREATE INDEX ts_oi_idx ON token_scope(owner_id); From 44b24af4662d970cec4172bfd046aaa237cc106c Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sat, 11 Mar 2017 17:19:55 -0500 Subject: [PATCH 148/269] database storage for device flow --- .../org/mitre/oauth2/model/DeviceCode.java | 15 +++ .../repository/impl/DeviceCodeRepository.java | 59 ++++++++++++ .../db/hsql/hsql_database_tables.sql | 2 +- .../impl/JpaDeviceCodeRepository.java | 96 +++++++++++++++++++ ...ice.java => DefaultDeviceCodeService.java} | 48 +++++----- .../org/mitre/oauth2/web/DeviceEndpoint.java | 16 ++-- 6 files changed, 207 insertions(+), 29 deletions(-) create mode 100644 openid-connect-common/src/main/java/org/mitre/oauth2/repository/impl/DeviceCodeRepository.java create mode 100644 openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java rename openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/{InMemoryDeviceCodeService.java => DefaultDeviceCodeService.java} (75%) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java index 8f7486b9fe..a886729c30 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java @@ -33,6 +33,8 @@ import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.MapKeyColumn; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; import javax.persistence.Table; import javax.persistence.Temporal; @@ -42,8 +44,21 @@ */ @Entity @Table(name = "device_code") +@NamedQueries({ + @NamedQuery(name = DeviceCode.QUERY_BY_USER_CODE, query = "select d from DeviceCode d where d.userCode = :" + DeviceCode.PARAM_USER_CODE), + @NamedQuery(name = DeviceCode.QUERY_BY_DEVICE_CODE, query = "select d from DeviceCode d where d.deviceCode = :" + DeviceCode.PARAM_DEVICE_CODE), + @NamedQuery(name = DeviceCode.QUERY_EXPIRED_BY_DATE, query = "select d from DeviceCode d where d.expiration <= :" + DeviceCode.PARAM_DATE) +}) public class DeviceCode { + public static final String QUERY_BY_USER_CODE = "DeviceCode.queryByUserCode"; + public static final String QUERY_BY_DEVICE_CODE = "DeviceCode.queryByDeviceCode"; + public static final String QUERY_EXPIRED_BY_DATE = "DeviceCode.queryExpiredByDate"; + + public static final String PARAM_USER_CODE = "userCode"; + public static final String PARAM_DEVICE_CODE = "deviceCode"; + public static final String PARAM_DATE = "date"; + private Long id; private String deviceCode; private String userCode; diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/impl/DeviceCodeRepository.java b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/impl/DeviceCodeRepository.java new file mode 100644 index 0000000000..955cf6773d --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/impl/DeviceCodeRepository.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.oauth2.repository.impl; + +import java.util.Set; + +import org.mitre.oauth2.model.DeviceCode; + +/** + * @author jricher + * + */ +public interface DeviceCodeRepository { + + /** + * @param id + * @return + */ + public DeviceCode getById(Long id); + + /** + * @param deviceCode + * @return + */ + public DeviceCode getByDeviceCode(String deviceCode); + + /** + * @param scope + */ + public void remove(DeviceCode scope); + + /** + * @param scope + * @return + */ + public DeviceCode save(DeviceCode scope); + + /** + * @param userCode + * @return + */ + public DeviceCode getByUserCode(String userCode); + +} diff --git a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql index 8f5e69ce7c..27d9c698db 100644 --- a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql @@ -131,7 +131,7 @@ CREATE TABLE IF NOT EXISTS client_details ( dynamically_registered BOOLEAN DEFAULT false NOT NULL, allow_introspection BOOLEAN DEFAULT false NOT NULL, id_token_validity_seconds BIGINT DEFAULT 600 NOT NULL, - device_code_validity_seconds BIGINT DEFAULT 600 NOT NULL, + device_code_validity_seconds BIGINT, client_id VARCHAR(256), client_secret VARCHAR(2048), diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java new file mode 100644 index 0000000000..a72555040b --- /dev/null +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.oauth2.repository.impl; + +import static org.mitre.util.jpa.JpaUtil.getSingleResult; +import static org.mitre.util.jpa.JpaUtil.saveOrUpdate; + +import java.util.LinkedHashSet; +import java.util.Set; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.TypedQuery; + +import org.mitre.oauth2.model.DeviceCode; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author jricher + * + */ +@Repository("jpaDeviceCodeRepository") +public class JpaDeviceCodeRepository implements DeviceCodeRepository { + + @PersistenceContext(unitName="defaultPersistenceUnit") + private EntityManager em; + + /* (non-Javadoc) + */ + @Override + @Transactional(value="defaultTransactionManager") + public DeviceCode getById(Long id) { + return em.find(DeviceCode.class, id); + } + + /* (non-Javadoc) + */ + @Override + @Transactional(value="defaultTransactionManager") + public DeviceCode getByUserCode(String value) { + TypedQuery query = em.createNamedQuery(DeviceCode.QUERY_BY_USER_CODE, DeviceCode.class); + query.setParameter(DeviceCode.PARAM_USER_CODE, value); + return getSingleResult(query.getResultList()); + } + + /* (non-Javadoc) + */ + @Override + @Transactional(value="defaultTransactionManager") + public DeviceCode getByDeviceCode(String value) { + TypedQuery query = em.createNamedQuery(DeviceCode.QUERY_BY_DEVICE_CODE, DeviceCode.class); + query.setParameter(DeviceCode.PARAM_DEVICE_CODE, value); + return getSingleResult(query.getResultList()); + } + + /* (non-Javadoc) + */ + @Override + @Transactional(value="defaultTransactionManager") + public void remove(DeviceCode scope) { + DeviceCode found = getById(scope.getId()); + + if (found != null) { + em.remove(found); + } + + } + + /* (non-Javadoc) + * @see org.mitre.oauth2.repository.SystemScopeRepository#save(org.mitre.oauth2.model.SystemScope) + */ + @Override + @Transactional(value="defaultTransactionManager") + public DeviceCode save(DeviceCode scope) { + return saveOrUpdate(scope.getId(), em, scope); + } + +} diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/InMemoryDeviceCodeService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java similarity index 75% rename from openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/InMemoryDeviceCodeService.java rename to openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java index eb522c5b69..0ef3677781 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/InMemoryDeviceCodeService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java @@ -18,15 +18,15 @@ package org.mitre.oauth2.service.impl; import java.util.Date; -import java.util.HashSet; import java.util.Map; import java.util.Set; import org.mitre.oauth2.model.AuthenticationHolderEntity; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.DeviceCode; +import org.mitre.oauth2.repository.impl.DeviceCodeRepository; import org.mitre.oauth2.service.DeviceCodeService; -import org.springframework.security.core.Authentication; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.stereotype.Service; @@ -35,10 +35,11 @@ * @author jricher * */ -@Service -public class InMemoryDeviceCodeService implements DeviceCodeService { +@Service("defaultDeviceCodeService") +public class DefaultDeviceCodeService implements DeviceCodeService { - private Set codes = new HashSet<>(); + @Autowired + private DeviceCodeRepository repository; /* (non-Javadoc) * @see org.mitre.oauth2.service.DeviceCodeService#save(org.mitre.oauth2.model.DeviceCode) @@ -54,8 +55,7 @@ public DeviceCode createNewDeviceCode(String deviceCode, String userCode, Set response = new HashMap<>(); + response.put("device_code", deviceCode); + response.put("user_code", userCode); + response.put("verification_uri", config.getIssuer() + USER_URL); + if (client.getDeviceCodeValiditySeconds() != null) { + response.put("expires_in", client.getDeviceCodeValiditySeconds()); + } + + model.put(JsonEntityView.ENTITY, response); return JsonEntityView.VIEWNAME; From d83fbc6c67a5932b3c834593e9ef20717b9787f4 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sat, 11 Mar 2017 17:57:06 -0500 Subject: [PATCH 149/269] UI for managing device code flow --- .../src/main/webapp/resources/js/client.js | 11 +++++++-- .../resources/js/locale/en/messages.json | 2 ++ .../webapp/resources/template/client.html | 24 ++++++++++++++++++- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js index e59eaaba36..dcdd57bd14 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js @@ -93,6 +93,7 @@ var ClientModel = Backbone.Model.extend({ dynamicallyRegistered:false, allowIntrospection:false, idTokenValiditySeconds: null, + deviceCodeValiditySeconds: null, createdAt:null, allowRefresh:false, @@ -790,7 +791,8 @@ var ClientFormView = Backbone.View.extend({ 'implicit': 'implicit', 'client_credentials': 'client_credentials', 'redelegate': 'urn:ietf:params:oauth:grant_type:redelegate', - 'refresh_token': 'refresh_token' + 'refresh_token': 'refresh_token', + 'device': 'urn:ietf:params:oauth:grant-type:device_code' }, // maps from a form-friendly name to the real response type parameter name @@ -863,7 +865,9 @@ var ClientFormView = Backbone.View.extend({ accessTokenValiditySeconds = this.getFormTokenNumberValue($('#accessTokenValidityTime input[type=text]').val(), $('#accessTokenValidityTime select').val()); } - var idTokenValiditySeconds = this.getFormTokenNumberValue($('#idTokenValidityTime input[type=text]').val(), $('#idTokenValidityTime select').val()); + var idTokenValiditySeconds = this.getFormTokenNumberValue($('#idTokenValidityTime input[type=text]').val(), $('#idTokenValidityTime select').val()); + + var deviceCodeValiditySeconds = this.getFormTokenNumberValue($('#deviceCodeValidityTime input[type=text]').val, $('#deviceCodeValidityTime select').val()); var refreshTokenValiditySeconds = null; if ($('#allowRefresh').is(':checked')) { @@ -928,6 +932,7 @@ var ClientFormView = Backbone.View.extend({ accessTokenValiditySeconds: accessTokenValiditySeconds, refreshTokenValiditySeconds: refreshTokenValiditySeconds, idTokenValiditySeconds: idTokenValiditySeconds, + deviceCodeValiditySeconds: deviceCodeValiditySeconds, allowRefresh: $('#allowRefresh').is(':checked'), allowIntrospection: $('#allowIntrospection input').is(':checked'), // <-- And here? --^ scope: scopes, @@ -1235,6 +1240,7 @@ ui.routes.push({path: "admin/client/new", name: "newClient", callback: accessTokenValiditySeconds:3600, refreshTokenValiditySeconds:24*3600, idTokenValiditySeconds:300, + deviceCodeValiditySeconds:30*60, grantTypes: ["authorization_code"], responseTypes: ["code"], subjectType: "PUBLIC", @@ -1252,6 +1258,7 @@ ui.routes.push({path: "admin/client/new", name: "newClient", callback: scope: _.uniq(_.flatten(app.systemScopeList.defaultScopes().pluck("value"))), accessTokenValiditySeconds:3600, idTokenValiditySeconds:600, + deviceCodeValiditySeconds:30*60, grantTypes: ["authorization_code"], responseTypes: ["code"], subjectType: "PUBLIC", diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json index 99329298ce..eb04ebbc1c 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json @@ -64,6 +64,8 @@ "rsa1-5": "RSAES-PKCS1-V1_5" }, "cryptography": "Crypto", + "device": "device", + "device-code-timeout": "Device Code Timeout", "display-secret": "Display/edit client secret:", "edit": "Edit Client", "generate-new-secret": "Generate a new client secret?", diff --git a/openid-connect-server-webapp/src/main/webapp/resources/template/client.html b/openid-connect-server-webapp/src/main/webapp/resources/template/client.html index 94d6775570..7a46786a69 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/template/client.html +++ b/openid-connect-server-webapp/src/main/webapp/resources/template/client.html @@ -359,7 +359,13 @@

    <%-($.inArray("urn:ietf:params:oauth:grant_type:redelegate", client.grantTypes) > -1 ? 'checked' : '')%>> - + +
    + -1 ? 'checked' : '')%>> + +
    +
    @@ -611,6 +617,22 @@

    Enter this time in seconds, minutes, or hours.

    + +
    + +
    +
    + + +
    +

    Enter this time in seconds, minutes, or hours.

    +
    +
    +
    From 153776ecb5e09d05c6215d6ff5c3d6c93fe2f20f Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sat, 11 Mar 2017 17:57:23 -0500 Subject: [PATCH 150/269] =?UTF-8?q?Don=E2=80=99t=20catch=20OAuth2=20errors?= =?UTF-8?q?,=20let=20the=20framework=20handle=20them=20here?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java index 2ba7a808b4..86f14c3b25 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java @@ -110,10 +110,6 @@ public String requestDeviceCode(@RequestParam("client_id") String clientId, @Req throw new InvalidClientException("Unauthorized grant type: " + DeviceTokenGranter.GRANT_TYPE); } - } catch (OAuth2Exception e) { - logger.error("OAuth2Exception was thrown when attempting to load client", e); - model.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST); - return HttpCodeView.VIEWNAME; } catch (IllegalArgumentException e) { logger.error("IllegalArgumentException was thrown when attempting to load client", e); model.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST); From cbf5bf742b493d9784fa4b78a62ecf935230a17d Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 13 Mar 2017 11:32:39 -0400 Subject: [PATCH 151/269] added messages for display pages, better error handling in user-facing pages --- .../webapp/WEB-INF/views/deviceApproved.jsp | 3 +- .../webapp/WEB-INF/views/requestUserCode.jsp | 43 +++++++++++-------- .../resources/js/locale/en/messages.json | 13 +++++- .../org/mitre/oauth2/web/DeviceEndpoint.java | 26 +++++------ 4 files changed, 52 insertions(+), 33 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/deviceApproved.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/deviceApproved.jsp index a31898fe93..1b588f12ba 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/deviceApproved.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/deviceApproved.jsp @@ -23,10 +23,9 @@ - -
    +
    diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp index ec80d9b5dd..51292fff4b 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp @@ -15,29 +15,38 @@
    -

      - - - - - - - - - -

    + +

     

    + + + + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    +
    - - - + + - -   - +
    diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json index eb04ebbc1c..7eacdb1bc7 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json @@ -493,9 +493,20 @@ "device": { "request_code": { "title": "Enter Code", - "header": "Enter code for ", + "header": "Enter Code", "description": "Enter the code displayed on your device into the box below and press submit", "submit": "Submit" + }, + "error": { + "noUserCode": "The code that you entered was not found.", + "expiredUserCode": "The code that you entered has expired. Return to your device and request a new code.", + "userCodeAlreadyApproved": "The code that you entered has already been used.", + "userCodeMismatch": "There was an error processing the code you entered. Try refreshing the page and returning to your device to request a new code.", + "error": "There was an error processing the code you entered. Return to your device adn request a new code." + }, + "approve": { + "approved": "The device has been approved.", + "notApproved": "The device has not been approved." } } } \ No newline at end of file diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java index 86f14c3b25..fcf6e77783 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java @@ -178,20 +178,20 @@ public String readUserCode(@RequestParam("user_code") String userCode, ModelMap // we couldn't find the device code if (dc == null) { - // TODO: return error - return "error"; + model.addAttribute("error", "noUserCode"); + return "requestUserCode"; } // make sure the code hasn't expired yet if (dc.getExpiration() != null && dc.getExpiration().before(new Date())) { - // TODO: return an error - return "error"; + model.addAttribute("error", "expiredUserCode"); + return "requestUserCode"; } // make sure the device code hasn't already been approved if (dc.isApproved()) { - // TODO: return an error - return "error"; + model.addAttribute("error", "userCodeAlreadyApproved"); + return "requestUserCode"; } ClientDetailsEntity client = clientService.loadClientByClientId(dc.getClientId()); @@ -234,20 +234,20 @@ public String approveDevice(@RequestParam("user_code") String userCode, @Request // make sure the form that was submitted is the one that we were expecting if (!dc.getUserCode().equals(userCode)) { - // TODO: return an error - return "error"; + model.addAttribute("error", "userCodeMismatch"); + return "requestUserCode"; } // make sure the code hasn't expired yet if (dc.getExpiration() != null && dc.getExpiration().before(new Date())) { - // TODO: return an error - return "error"; + model.addAttribute("error", "expiredUserCode"); + return "requestUserCode"; } // user did not approve if (!approve) { - // TODO: return an error - return "error"; + model.addAttribute("approved", false); + return "deviceApproved"; } // create an OAuth request for storage @@ -277,7 +277,7 @@ public String approveDevice(@RequestParam("user_code") String userCode, @Request sortedScopes.addAll(Sets.difference(scopes, systemScopes)); model.put("scopes", sortedScopes); - + model.put("approved", true); return "deviceApproved"; } From f915196c2ea0d2ecf458d0316fb865f079c1e90b Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 13 Mar 2017 11:49:30 -0400 Subject: [PATCH 152/269] fix approval display --- .../src/main/webapp/WEB-INF/views/deviceApproved.jsp | 9 ++++++++- .../src/main/webapp/WEB-INF/views/requestUserCode.jsp | 8 ++++++-- .../main/java/org/mitre/oauth2/web/DeviceEndpoint.java | 8 ++++---- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/deviceApproved.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/deviceApproved.jsp index 1b588f12ba..80f601c633 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/deviceApproved.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/deviceApproved.jsp @@ -25,7 +25,14 @@ -
    + + +
    +
    + +
    +
    +
    diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp index 51292fff4b..c629d0b9f0 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp @@ -41,10 +41,14 @@ -
    +
    - +
    +
    + +
    +
    diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java index fcf6e77783..a794ff73bf 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java @@ -244,6 +244,10 @@ public String approveDevice(@RequestParam("user_code") String userCode, @Request return "requestUserCode"; } + ClientDetailsEntity client = clientService.loadClientByClientId(dc.getClientId()); + + model.put("client", client); + // user did not approve if (!approve) { model.addAttribute("approved", false); @@ -256,10 +260,6 @@ public String approveDevice(@RequestParam("user_code") String userCode, @Request DeviceCode approvedCode = deviceCodeService.approveDeviceCode(dc, o2Auth); - ClientDetailsEntity client = clientService.loadClientByClientId(dc.getClientId()); - - model.put("client", client); - // pre-process the scopes Set scopes = scopeService.fromStrings(dc.getScope()); From f54d44cd9dd3c70fc380997677205b58a95c1810 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 14 Mar 2017 16:47:45 -0400 Subject: [PATCH 153/269] added device code to discovery, moved device endpoints --- .../java/org/mitre/discovery/web/DiscoveryEndpoint.java | 6 ++++-- .../src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java b/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java index 0949e17335..1c034924b4 100644 --- a/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java @@ -26,6 +26,7 @@ import org.mitre.jwt.signer.service.JWTSigningAndValidationService; import org.mitre.oauth2.model.PKCEAlgorithm; import org.mitre.oauth2.service.SystemScopeService; +import org.mitre.oauth2.web.DeviceEndpoint; import org.mitre.oauth2.web.IntrospectionEndpoint; import org.mitre.oauth2.web.RevocationEndpoint; import org.mitre.openid.connect.config.ConfigurationPropertiesBean; @@ -301,7 +302,7 @@ OPTIONAL. JSON array containing a list of the JWS signing algorithms (alg values JWSAlgorithm.ES256, JWSAlgorithm.ES384, JWSAlgorithm.ES512, JWSAlgorithm.PS256, JWSAlgorithm.PS384, JWSAlgorithm.PS512, Algorithm.NONE); - ArrayList grantTypes = Lists.newArrayList("authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer", "client_credentials", "urn:ietf:params:oauth:grant_type:redelegate"); + ArrayList grantTypes = Lists.newArrayList("authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer", "client_credentials", "urn:ietf:params:oauth:grant_type:redelegate", "urn:ietf:params:oauth:grant-type:device_code"); Map m = new HashMap<>(); m.put("issuer", config.getIssuer()); @@ -366,7 +367,8 @@ OPTIONAL. JSON array containing a list of the JWS signing algorithms (alg values m.put("revocation_endpoint", baseUrl + RevocationEndpoint.URL); // token revocation endpoint m.put("code_challenge_methods_supported", Lists.newArrayList(PKCEAlgorithm.plain.getName(), PKCEAlgorithm.S256.getName())); - + + m.put("device_authorization_endpoint", DeviceEndpoint.URL); model.addAttribute(JsonEntityView.ENTITY, m); diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java index a794ff73bf..a14f7dcb0b 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java @@ -73,8 +73,8 @@ @Controller public class DeviceEndpoint { - public static final String URL = "device"; - public static final String USER_URL = "device-user"; + public static final String URL = "devicecode"; + public static final String USER_URL = "device"; public static final Logger logger = LoggerFactory.getLogger(DeviceEndpoint.class); From f0c8b1b23554474185da7e1378b767c359442bf4 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 14 Mar 2017 17:38:46 -0400 Subject: [PATCH 154/269] fixed device code admin UI --- .../src/main/webapp/resources/js/client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js index dcdd57bd14..cb963cd11a 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js @@ -867,7 +867,7 @@ var ClientFormView = Backbone.View.extend({ var idTokenValiditySeconds = this.getFormTokenNumberValue($('#idTokenValidityTime input[type=text]').val(), $('#idTokenValidityTime select').val()); - var deviceCodeValiditySeconds = this.getFormTokenNumberValue($('#deviceCodeValidityTime input[type=text]').val, $('#deviceCodeValidityTime select').val()); + var deviceCodeValiditySeconds = this.getFormTokenNumberValue($('#deviceCodeValidityTime input[type=text]').val(), $('#deviceCodeValidityTime select').val()); var refreshTokenValiditySeconds = null; if ($('#allowRefresh').is(':checked')) { From 8406a89fd12a09929200beadf3ec5459c5b5057a Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 14 Mar 2017 17:39:57 -0400 Subject: [PATCH 155/269] added device flow expiration --- .../repository/impl/DeviceCodeRepository.java | 6 +++++ .../oauth2/service/DeviceCodeService.java | 4 +++- .../src/main/webapp/WEB-INF/task-config.xml | 1 + .../impl/JpaDeviceCodeRepository.java | 13 ++++++++++ .../impl/DefaultDeviceCodeService.java | 24 +++++++++++++++++++ 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/impl/DeviceCodeRepository.java b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/impl/DeviceCodeRepository.java index 955cf6773d..011fd24c2a 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/impl/DeviceCodeRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/impl/DeviceCodeRepository.java @@ -17,6 +17,7 @@ package org.mitre.oauth2.repository.impl; +import java.util.Collection; import java.util.Set; import org.mitre.oauth2.model.DeviceCode; @@ -56,4 +57,9 @@ public interface DeviceCodeRepository { */ public DeviceCode getByUserCode(String userCode); + /** + * @return + */ + public Collection getExpiredCodes(); + } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java index 6d5810e204..958dbc1d51 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java @@ -60,5 +60,7 @@ public interface DeviceCodeService { * @return */ public DeviceCode createNewDeviceCode(String deviceCode, String userCode, Set requestedScopes, ClientDetailsEntity client, Map parameters); - + + + public void clearExpiredDeviceCodes(); } diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/task-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/task-config.xml index 1b19c2152b..6f137dd48c 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/task-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/task-config.xml @@ -31,6 +31,7 @@ + diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java index a72555040b..91f34dfccf 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java @@ -22,6 +22,8 @@ import static org.mitre.util.jpa.JpaUtil.getSingleResult; import static org.mitre.util.jpa.JpaUtil.saveOrUpdate; +import java.util.Collection; +import java.util.Date; import java.util.LinkedHashSet; import java.util.Set; @@ -93,4 +95,15 @@ public DeviceCode save(DeviceCode scope) { return saveOrUpdate(scope.getId(), em, scope); } + /* (non-Javadoc) + * @see org.mitre.oauth2.repository.impl.DeviceCodeRepository#getExpiredCodes() + */ + @Override + @Transactional(value="defaultTransactionManager") + public Collection getExpiredCodes() { + TypedQuery query = em.createNamedQuery(DeviceCode.QUERY_EXPIRED_BY_DATE, DeviceCode.class); + query.setParameter(DeviceCode.PARAM_DATE, new Date()); + return query.getResultList(); + } + } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java index 0ef3677781..ecd4c1684a 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java @@ -17,11 +17,14 @@ package org.mitre.oauth2.service.impl; +import java.util.Collection; import java.util.Date; import java.util.Map; import java.util.Set; +import org.mitre.data.AbstractPageOperationTemplate; import org.mitre.oauth2.model.AuthenticationHolderEntity; +import org.mitre.oauth2.model.AuthorizationCodeEntity; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.DeviceCode; import org.mitre.oauth2.repository.impl.DeviceCodeRepository; @@ -30,6 +33,7 @@ import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; /** * @author jricher @@ -103,4 +107,24 @@ public DeviceCode consumeDeviceCode(String deviceCode, ClientDetails client) { } + /* (non-Javadoc) + * @see org.mitre.oauth2.service.DeviceCodeService#clearExpiredDeviceCodes() + */ + @Override + @Transactional(value="defaultTransactionManager") + public void clearExpiredDeviceCodes() { + + new AbstractPageOperationTemplate("clearExpiredDeviceCodes"){ + @Override + public Collection fetchPage() { + return repository.getExpiredCodes(); + } + + @Override + protected void doOperation(DeviceCode item) { + repository.remove(item); + } + }.execute(); + } + } From 22a4addfc029c756b560e19367088f1a6c51b7e8 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 15 Mar 2017 16:47:04 -0400 Subject: [PATCH 156/269] added software ID and software version --- .../oauth2/model/ClientDetailsEntity.java | 34 +++++++++++++++++++ .../mitre/oauth2/model/RegisteredClient.java | 32 +++++++++++++++++ .../oauth2/model/RegisteredClientFields.java | 3 +- .../ClientDetailsEntityJsonProcessor.java | 8 +++++ .../db/hsql/hsql_database_tables.sql | 2 ++ .../resources/js/locale/en/messages.json | 6 ++++ 6 files changed, 84 insertions(+), 1 deletion(-) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java index ed9e511ba1..2a4a0ea80f 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java @@ -103,6 +103,8 @@ public class ClientDetailsEntity implements ClientDetails { private String policyUri; private String jwksUri; // URI pointer to keys private JWKSet jwks; // public key stored by value + private String softwareId; + private String softwareVersion; /** Fields from OIDC Client Registration Specification **/ private AppType applicationType; // application_type @@ -1049,4 +1051,36 @@ public void setDeviceCodeValiditySeconds(Integer deviceCodeValiditySeconds) { this.deviceCodeValiditySeconds = deviceCodeValiditySeconds; } + /** + * @return the softwareId + */ + @Basic + @Column(name="software_id") + public String getSoftwareId() { + return softwareId; + } + + /** + * @param softwareId the softwareId to set + */ + public void setSoftwareId(String softwareId) { + this.softwareId = softwareId; + } + + /** + * @return the softwareVersion + */ + @Basic + @Column(name="software_version") + public String getSoftwareVersion() { + return softwareVersion; + } + + /** + * @param softwareVersion the softwareVersion to set + */ + public void setSoftwareVersion(String softwareVersion) { + this.softwareVersion = softwareVersion; + } + } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java index f5e195899d..83baeb1548 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java @@ -862,6 +862,38 @@ public void setDeviceCodeValiditySeconds(Integer deviceCodeValiditySeconds) { client.setDeviceCodeValiditySeconds(deviceCodeValiditySeconds); } + /** + * @return + * @see org.mitre.oauth2.model.ClientDetailsEntity#getSoftwareId() + */ + public String getSoftwareId() { + return client.getSoftwareId(); + } + + /** + * @param softwareId + * @see org.mitre.oauth2.model.ClientDetailsEntity#setSoftwareId(java.lang.String) + */ + public void setSoftwareId(String softwareId) { + client.setSoftwareId(softwareId); + } + + /** + * @return + * @see org.mitre.oauth2.model.ClientDetailsEntity#getSoftwareVersion() + */ + public String getSoftwareVersion() { + return client.getSoftwareVersion(); + } + + /** + * @param softwareVersion + * @see org.mitre.oauth2.model.ClientDetailsEntity#setSoftwareVersion(java.lang.String) + */ + public void setSoftwareVersion(String softwareVersion) { + client.setSoftwareVersion(softwareVersion); + } + } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClientFields.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClientFields.java index 4f27707d2f..fe5f37e8cf 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClientFields.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClientFields.java @@ -17,6 +17,8 @@ package org.mitre.oauth2.model; public interface RegisteredClientFields { + public String SOFTWARE_ID = "software_id"; + public String SOFTWARE_VERSION = "software_version"; public String SOFTWARE_STATEMENT = "software_statement"; public String CLAIMS_REDIRECT_URIS = "claims_redirect_uris"; public String CLIENT_SECRET_EXPIRES_AT = "client_secret_expires_at"; @@ -57,5 +59,4 @@ public interface RegisteredClientFields { public String CLIENT_SECRET = "client_secret"; public String CLIENT_ID = "client_id"; public String CODE_CHALLENGE_METHOD = "code_challenge_method"; - } diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/ClientDetailsEntityJsonProcessor.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/ClientDetailsEntityJsonProcessor.java index feb3fce747..6957d685b0 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/ClientDetailsEntityJsonProcessor.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/ClientDetailsEntityJsonProcessor.java @@ -82,7 +82,9 @@ import static org.mitre.oauth2.model.RegisteredClientFields.SCOPE; import static org.mitre.oauth2.model.RegisteredClientFields.SCOPE_SEPARATOR; import static org.mitre.oauth2.model.RegisteredClientFields.SECTOR_IDENTIFIER_URI; +import static org.mitre.oauth2.model.RegisteredClientFields.SOFTWARE_ID; import static org.mitre.oauth2.model.RegisteredClientFields.SOFTWARE_STATEMENT; +import static org.mitre.oauth2.model.RegisteredClientFields.SOFTWARE_VERSION; import static org.mitre.oauth2.model.RegisteredClientFields.SUBJECT_TYPE; import static org.mitre.oauth2.model.RegisteredClientFields.TOKEN_ENDPOINT_AUTH_METHOD; import static org.mitre.oauth2.model.RegisteredClientFields.TOKEN_ENDPOINT_AUTH_SIGNING_ALG; @@ -206,6 +208,9 @@ public static ClientDetailsEntity parse(JsonElement jsonEl) { c.setCodeChallengeMethod(getAsPkceAlgorithm(o, CODE_CHALLENGE_METHOD)); + c.setSoftwareId(getAsString(o, SOFTWARE_ID)); + c.setSoftwareVersion(getAsString(o, SOFTWARE_VERSION)); + // note that this does not process or validate the software statement, that's handled in other components String softwareStatement = getAsString(o, SOFTWARE_STATEMENT); if (!Strings.isNullOrEmpty(softwareStatement)) { @@ -345,6 +350,9 @@ public static JsonObject serialize(RegisteredClient c) { o.addProperty(CODE_CHALLENGE_METHOD, c.getCodeChallengeMethod() != null ? c.getCodeChallengeMethod().getName() : null); + o.addProperty(SOFTWARE_ID, c.getSoftwareId()); + o.addProperty(SOFTWARE_VERSION, c.getSoftwareVersion()); + if (c.getSoftwareStatement() != null) { o.addProperty(SOFTWARE_STATEMENT, c.getSoftwareStatement().serialize()); } diff --git a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql index 27d9c698db..00851a775e 100644 --- a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql @@ -171,6 +171,8 @@ CREATE TABLE IF NOT EXISTS client_details ( clear_access_tokens_on_refresh BOOLEAN DEFAULT true NOT NULL, software_statement VARCHAR(4096), + software_id VARCHAR(2048), + software_version VARCHAR(2048), code_challenge_method VARCHAR(256), diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json index 7eacdb1bc7..6cc12771dc 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json @@ -160,9 +160,15 @@ "ps384": "RSASSA-PSS using SHA-384 and MGF1 with SHA-384", "ps512": "RSASSA-PSS using SHA-512 and MGF1 with SHA-512" }, + "software-id": "Software ID", + "software-id-placeholder": "software ID...", + "software-id-help": "Identifier for the software in this client", "software-statement": "Software Statement", "software-statement-placeholder": "eyj0...", "software-statement-help": "A software statement is issued by a trusted third party and locks certain elements of a client's registration", + "software-version": "Software Version", + "software-version-placeholder": "1.0...", + "software-version-help": "Version of the software in this client", "subject-type": "Subject Type", "terms": "Terms of Service", "terms-help": "URL for the Terms of Service of this client, will be displayed to the user", From e57ea488b305e70d0b6d485a580ee815b52e2829 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 15 Mar 2017 17:30:26 -0400 Subject: [PATCH 157/269] fixed null handling on client admin pages, added software ID and version to UI --- .../src/main/webapp/resources/js/client.js | 55 ++++++++++------ .../src/main/webapp/resources/js/dynreg.js | 29 ++++++-- .../resources/js/locale/en/messages.json | 2 +- .../webapp/resources/template/client.html | 66 ++++++++++++------- .../webapp/resources/template/dynreg.html | 40 +++++++---- 5 files changed, 126 insertions(+), 66 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js index cb963cd11a..2ec5708d58 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js @@ -34,26 +34,26 @@ var ClientModel = Backbone.Model.extend({ defaults:{ id:null, - clientId:"", - clientSecret:"", + clientId:null, + clientSecret:null, redirectUris:[], clientName:null, - clientUri:"", - logoUri:"", + clientUri:null, + logoUri:null, contacts:[], - tosUri:"", + tosUri:null, tokenEndpointAuthMethod:null, scope:[], grantTypes:[], responseTypes:[], - policyUri:"", + policyUri:null, - jwksUri:"", + jwksUri:null, jwks:null, jwksType:"URI", applicationType:null, - sectorIdentifierUri:"", + sectorIdentifierUri:null, subjectType:null, requestObjectSigningAlg:null, @@ -72,11 +72,15 @@ var ClientModel = Backbone.Model.extend({ requireAuthTime:false, defaultACRvalues:null, - initiateLoginUri:"", + initiateLoginUri:null, postLogoutRedirectUris:[], requestUris:[], + softwareStatement:null, + softwareId:null, + softwareVersion:null, + codeChallengeMethod:null, authorities:[], @@ -87,7 +91,7 @@ var ClientModel = Backbone.Model.extend({ claimsRedirectUris:[], - clientDescription:"", + clientDescription:null, reuseRefreshToken:true, clearAccessTokensOnRefresh:true, dynamicallyRegistered:false, @@ -769,6 +773,15 @@ var ClientFormView = Backbone.View.extend({ } }, + // returns "null" if the given value is falsy + emptyToNull:function(value) { + if (value) { + return value; + } else { + return null; + } + }, + disableUnsupportedJOSEItems:function(serverSupported, query) { var supported = ['default']; if (serverSupported) { @@ -921,33 +934,35 @@ var ClientFormView = Backbone.View.extend({ var attrs = { - clientName:$('#clientName input').val(), - clientId:$('#clientId input').val(), + clientName:this.emptyToNull($('#clientName input').val()), + clientId:this.emptyToNull($('#clientId input').val()), clientSecret: clientSecret, generateClientSecret:generateClientSecret, redirectUris: redirectUris, - clientDescription:$('#clientDescription textarea').val(), - logoUri:$('#logoUri input').val(), + clientDescription:this.emptyToNull($('#clientDescription textarea').val()), + logoUri:this.emptyToNull($('#logoUri input').val()), grantTypes: grantTypes, accessTokenValiditySeconds: accessTokenValiditySeconds, refreshTokenValiditySeconds: refreshTokenValiditySeconds, idTokenValiditySeconds: idTokenValiditySeconds, deviceCodeValiditySeconds: deviceCodeValiditySeconds, allowRefresh: $('#allowRefresh').is(':checked'), - allowIntrospection: $('#allowIntrospection input').is(':checked'), // <-- And here? --^ + allowIntrospection: $('#allowIntrospection input').is(':checked'), scope: scopes, - tosUri: $('#tosUri input').val(), - policyUri: $('#policyUri input').val(), - clientUri: $('#clientUri input').val(), + tosUri: this.emptyToNull($('#tosUri input').val()), + policyUri: this.emptyToNull($('#policyUri input').val()), + clientUri: this.emptyToNull($('#clientUri input').val()), applicationType: $('#applicationType input').filter(':checked').val(), jwksUri: jwksUri, jwks: jwks, subjectType: subjectType, - softwareStatement: $('#softwareStatement textarea').val(), + softwareStatement: this.emptyToNull($('#softwareStatement textarea').val()), + softwareId: this.emptyToNull($('#softwareId input').val()), + softwareVersion: this.emptyToNull($('#softwareVersion input').val()), tokenEndpointAuthMethod: tokenEndpointAuthMethod, responseTypes: responseTypes, sectorIdentifierUri: sectorIdentifierUri, - initiateLoginUri: $('#initiateLoginUri input').val(), + initiateLoginUri: this.emptyToNull($('#initiateLoginUri input').val()), postLogoutRedirectUris: this.postLogoutRedirectUrisCollection.pluck('item'), claimsRedirectUris: this.claimsRedirectUrisCollection.pluck('item'), reuseRefreshToken: $('#reuseRefreshToken').is(':checked'), diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js b/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js index 9760ef535f..dca9fe2f25 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js @@ -61,6 +61,10 @@ var DynRegClient = Backbone.Model.extend({ request_uris:[], + software_statement:null, + software_id:null, + software_version:null, + code_challenge_method:null, registration_access_token:null, @@ -313,6 +317,15 @@ var DynRegEditView = Backbone.View.extend({ } }, + // returns "null" if the given value is falsy + emptyToNull:function(value) { + if (value) { + return value; + } else { + return null; + } + }, + // maps from a form-friendly name to the real grant parameter name grantMap:{ 'authorization_code': 'authorization_code', @@ -405,24 +418,26 @@ var DynRegEditView = Backbone.View.extend({ } var attrs = { - client_name:$('#clientName input').val(), + client_name:this.emptyToNull($('#clientName input').val()), redirect_uris: redirectUris, - logo_uri:$('#logoUri input').val(), + logo_uri:this.emptyToNull($('#logoUri input').val()), grant_types: grantTypes, scope: scopes, client_secret: null, // never send a client secret - tos_uri: $('#tosUri input').val(), - policy_uri: $('#policyUri input').val(), - client_uri: $('#clientUri input').val(), + tos_uri: this.emptyToNull($('#tosUri input').val()), + policy_uri: this.emptyToNull($('#policyUri input').val()), + client_uri: this.emptyToNull($('#clientUri input').val()), application_type: $('#applicationType input').filter(':checked').val(), jwks_uri: jwksUri, jwks: jwks, subject_type: subjectType, - software_statement: $('#softwareStatement textarea').val(), + software_statement: this.emptyToNull($('#softwareStatement textarea').val()), + softwareId: this.emptyToNull($('#softwareId input').val()), + softwareVersion: this.emptyToNull($('#softwareVersion input').val()), token_endpoint_auth_method: $('#tokenEndpointAuthMethod input').filter(':checked').val(), response_types: responseTypes, sector_identifier_uri: sectorIdentifierUri, - initiate_login_uri: $('#initiateLoginUri input').val(), + initiate_login_uri: this.emptyToNull($('#initiateLoginUri input').val()), post_logout_redirect_uris: this.postLogoutRedirectUrisCollection.pluck('item'), claims_redirect_uris: this.claimsRedirectUrisCollection.pluck('item'), require_auth_time: $('#requireAuthTime input').is(':checked'), diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json index 6cc12771dc..1277794e4b 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json @@ -37,7 +37,7 @@ "client-description-placeholder": "Type a description", "client-id": "Client ID", "client-id-help": "Unique identifier. If you leave this blank it will be automatically generated.", - "client-id-placeholder": "Type something", + "client-id-placeholder": "Client ID will be automatically generated", "client-name": "Client name", "client-name-help": "Human-readable application name", "client-name-placeholder": "Type something", diff --git a/openid-connect-server-webapp/src/main/webapp/resources/template/client.html b/openid-connect-server-webapp/src/main/webapp/resources/template/client.html index 7a46786a69..b2de62da99 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/template/client.html +++ b/openid-connect-server-webapp/src/main/webapp/resources/template/client.html @@ -215,7 +215,7 @@

    - +

    Human-readable application name

    @@ -223,7 +223,7 @@

    - +

    Unique identifier. If you leave this blank it will be automatically generated.

    @@ -246,7 +246,7 @@

    - +

    URL that points to a logo image, will be displayed on approval page

    @@ -260,7 +260,7 @@

    - +

    URL for the Terms of Service of this client, will be displayed to the user

    @@ -268,7 +268,7 @@

    - +

    URL for the Policy Statement of this client, will be displayed to the user

    @@ -276,23 +276,27 @@

    - +

    URL for the client's home page, will be displayed to the user

    -
    - -
    -
    - > - - > - -
    -
    -
    - +
    + +
    + +

    Identifier for the software in this client

    +
    +
    + +
    + +
    + +

    Version of the software in this client

    +
    +
    +
    @@ -428,7 +432,7 @@

    - +

    Sector Identifier for JavaScript

    @@ -490,7 +494,7 @@

    - +
    Generate on Save @@ -535,11 +539,11 @@

    - +

    URL for the client's JSON Web Key set (must be reachable by the server)

    - +

    Key set value (must be a valid JWK Set formatted key)

    @@ -784,7 +788,7 @@

    - +

    URL to initiate login on the client

    @@ -815,7 +819,7 @@

    - +

    Default maximum session age before re-prompting

    @@ -832,7 +836,19 @@

    -
    +
    + +
    +
    + > + + > + +
    +
    +
    + +
    diff --git a/openid-connect-server-webapp/src/main/webapp/resources/template/dynreg.html b/openid-connect-server-webapp/src/main/webapp/resources/template/dynreg.html index ffed2638a8..11696ba72b 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/template/dynreg.html +++ b/openid-connect-server-webapp/src/main/webapp/resources/template/dynreg.html @@ -191,18 +191,22 @@

    -
    - -
    - - -
    -
    - +
    + +
    + +

    Identifier for the software in this client

    +
    +
    + +
    + +
    + +

    Version of the software in this client

    +
    +
    +
    @@ -578,7 +582,17 @@

    - +
    + +
    + + +
    +
    From 02928b048f54cc294a9e7aa7e6981ced6e963c34 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 15 Mar 2017 17:38:46 -0400 Subject: [PATCH 158/269] added software ID and version to data API --- .../connect/service/impl/MITREidDataService_1_3.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java index be2fb65f1e..caa0110346 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java @@ -149,6 +149,8 @@ public class MITREidDataService_1_3 extends MITREidDataServiceSupport implements private static final String ID = "id"; private static final String CODE_CHALLENGE_METHOD = "codeChallengeMethod"; private static final String SOFTWARE_STATEMENT = "softwareStatement"; + private static final String SOFTWARE_VERSION = "softwareVersion"; + private static final String SOFTWARE_ID = "softwareId"; /** * Logger for this class @@ -524,6 +526,8 @@ private void writeClients(JsonWriter writer) { writer.name(CLEAR_ACCESS_TOKENS_ON_REFRESH).value(client.isClearAccessTokensOnRefresh()); writer.name(DYNAMICALLY_REGISTERED).value(client.isDynamicallyRegistered()); writer.name(CODE_CHALLENGE_METHOD).value(client.getCodeChallengeMethod() != null ? client.getCodeChallengeMethod().getName() : null); + writer.name(SOFTWARE_ID).value(client.getSoftwareId()); + writer.name(SOFTWARE_VERSION).value(client.getSoftwareVersion()); writer.name(SOFTWARE_STATEMENT).value(client.getSoftwareStatement() != null ? client.getSoftwareStatement().serialize() : null); writer.endObject(); logger.debug("Wrote client {}", client.getId()); @@ -1155,6 +1159,10 @@ private void readClients(JsonReader reader) throws IOException { client.setDynamicallyRegistered(reader.nextBoolean()); } else if (name.equals(CODE_CHALLENGE_METHOD)) { client.setCodeChallengeMethod(PKCEAlgorithm.parse(reader.nextString())); + } else if (name.equals(SOFTWARE_ID)) { + client.setSoftwareId(reader.nextString()); + } else if (name.equals(SOFTWARE_VERSION)) { + client.setSoftwareVersion(reader.nextString()); } else if (name.equals(SOFTWARE_STATEMENT)) { try { client.setSoftwareStatement(JWTParser.parse(reader.nextString())); From 626b18d5cab885678e250f5c35dbade79d2326c4 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 16 Mar 2017 13:51:19 -0400 Subject: [PATCH 159/269] added software id and version to UI search --- .../src/main/webapp/resources/js/client.js | 6 ++++++ .../src/main/webapp/resources/js/locale/en/messages.json | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js index 2ec5708d58..5e9f662022 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js @@ -157,6 +157,12 @@ var ClientModel = Backbone.Model.extend({ matches.push($.t('client.client-table.match.scope')); } } + if (this.get('softwareId') != null && this.get('softwareId').toLowerCase().indexOf(term.toLowerCase()) != -1) { + matches.push($.t('client.client-table.match.software-id')); + } + if (this.get('softwareVersion') != null && this.get('softwareVersion').toLowerCase().indexOf(term.toLowerCase()) != -1) { + matches.push($.t('client.client-table.match.software-version')); + } } else { // there's no search term, we always match diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json index 1277794e4b..1989f3cac0 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/locale/en/messages.json @@ -202,7 +202,9 @@ "policy": "policy", "redirect": "redirect uri", "scope": "scope", - "terms": "terms of service" + "terms": "terms of service", + "software-id": "software ID", + "software-version": "version" }, "matched-search": "Matched search:", "new": "New Client", From 256b79ae5121afc56af5d45887733374f87b19a4 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 16 Mar 2017 17:20:04 -0400 Subject: [PATCH 160/269] lazy load client stats in UI --- .../openid/connect/model/ClientStat.java | 42 +++++++ .../openid/connect/service/StatsService.java | 8 +- .../src/main/webapp/resources/js/admin.js | 8 -- .../src/main/webapp/resources/js/client.js | 111 +++++++++++++++--- .../webapp/resources/template/client.html | 20 ++-- .../service/impl/DefaultStatsService.java | 33 +++--- .../mitre/openid/connect/web/StatsAPI.java | 25 ++-- 7 files changed, 182 insertions(+), 65 deletions(-) create mode 100644 openid-connect-common/src/main/java/org/mitre/openid/connect/model/ClientStat.java diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ClientStat.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ClientStat.java new file mode 100644 index 0000000000..585df3b2b5 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ClientStat.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.openid.connect.model; + +/** + * @author jricher + * + */ +public class ClientStat { + + private Integer approvedSiteCount; + + /** + * @return the count + */ + public Integer getApprovedSiteCount() { + return approvedSiteCount; + } + + /** + * @param count the count to set + */ + public void setApprovedSiteCount(Integer count) { + this.approvedSiteCount = count; + } + +} diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/StatsService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/StatsService.java index a1e45b716d..8b2be24bb3 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/StatsService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/StatsService.java @@ -21,6 +21,8 @@ import java.util.Map; +import org.mitre.openid.connect.model.ClientStat; + /** * @author jricher * @@ -42,15 +44,15 @@ public interface StatsService { * * @return a map of id of client object to number of approvals */ - public Map getByClientId(); + public Map getByClientId(); /** * Calculate the usage count for a single client * - * @param id the id of the client to search on + * @param clientId the id of the client to search on * @return */ - public Integer getCountForClientId(Long id); + public ClientStat getCountForClientId(String clientId); /** * Trigger the stats to be recalculated upon next update. diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js b/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js index a329c1caa4..fdbacdfbb0 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js @@ -299,12 +299,6 @@ var BreadCrumbView = Backbone.View.extend({ }); -// Stats table - -var StatsModel = Backbone.Model.extend({ - url: "api/stats/byclientid" -}); - // User Profile var UserProfileView = Backbone.View.extend({ @@ -430,8 +424,6 @@ var AppRouter = Backbone.Router.extend({ initialize:function () { - this.clientStats = new StatsModel(); - this.breadCrumbView = new BreadCrumbView({ collection:new Backbone.Collection() }); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js index 5e9f662022..fceea87493 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js @@ -194,6 +194,10 @@ var RegistrationTokenModel = Backbone.Model.extend({ urlRoot: 'api/tokens/registration' }); +var ClientStatsModel = Backbone.Model.extend({ + urlRoot: 'api/stats/byclientid' +}); + var ClientCollection = Backbone.Collection.extend({ initialize: function() { @@ -216,6 +220,8 @@ var ClientCollection = Backbone.Collection.extend({ var ClientView = Backbone.View.extend({ tagName: 'tr', + + isRendered: false, initialize:function (options) { this.options = options; @@ -236,6 +242,10 @@ var ClientView = Backbone.View.extend({ this.registrationTokenTemplate = _.template($('#tmpl-client-registration-token').html()); } + if (!this.countTemplate) { + this.countTemplate = _.template($('#tmpl-client-count').html()); + } + this.model.bind('change', this.render, this); }, @@ -259,7 +269,7 @@ var ClientView = Backbone.View.extend({ } - var json = {client: this.model.toJSON(), count: this.options.count, whiteList: this.options.whiteList, + var json = {client: this.model.toJSON(), whiteList: this.options.whiteList, displayCreationDate: displayCreationDate, hoverCreationDate: hoverCreationDate}; this.$el.html(this.template(json)); @@ -273,10 +283,19 @@ var ClientView = Backbone.View.extend({ this.$('.allow-introspection').tooltip({title: $.t('client.client-table.allow-introspection-tooltip')}); this.updateMatched(); + this.updateStats(); + $(this.el).i18n(); + + this.isRendered = true; + return this; }, + updateStats:function(eventName) { + $('.count', this.el).html(this.countTemplate({count: this.options.clientStat.get('approvedSiteCount')})); + }, + showRegistrationToken:function(e) { e.preventDefault(); @@ -412,6 +431,8 @@ var ClientListView = Backbone.View.extend({ tagName: 'span', + stats: {}, + initialize:function (options) { this.options = options; this.filteredModel = this.model; @@ -420,7 +441,6 @@ var ClientListView = Backbone.View.extend({ load:function(callback) { if (this.model.isFetched && this.options.whiteListList.isFetched && - this.options.stats.isFetched && this.options.systemScopeList.isFetched) { callback(); return; @@ -430,13 +450,11 @@ var ClientListView = Backbone.View.extend({ $('#loading').html( '' + $.t("common.clients") + ' ' + '' + $.t("whitelist.whitelist") + ' ' + - '' + $.t("common.scopes") + ' ' + - '' + $.t("common.statistics") + ' ' + '' + $.t("common.scopes") + ' ' ); $.when(this.model.fetchIfNeeded({success:function(e) {$('#loading-clients').addClass('label-success');}, error:app.errorHandlerView.handleError()}), this.options.whiteListList.fetchIfNeeded({success:function(e) {$('#loading-whitelist').addClass('label-success');}, error:app.errorHandlerView.handleError()}), - this.options.stats.fetchIfNeeded({success:function(e) {$('#loading-stats').addClass('label-success');}, error:app.errorHandlerView.handleError()}), this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}, error:app.errorHandlerView.handleError()})) .done(function() { $('#loadingbox').sheet('hide'); @@ -471,27 +489,44 @@ var ClientListView = Backbone.View.extend({ renderInner:function(eventName) { - // render the rows + // set up the rows to render + // (note that this doesn't render until visibility is determined in togglePlaceholder) + _.each(this.filteredModel.models, function (client, index) { + var clientStat = this.getStat(client.get('clientId')); var view = new ClientView({ - model:client, - count:this.options.stats.get(client.get('id')), + model:client, + clientStat:clientStat, systemScopeList: this.options.systemScopeList, whiteList: this.options.whiteListList.getByClientId(client.get('clientId')) }); view.parentView = this; - var element = view.render().el; + //var element = view.render().el; + var element = view.el; $("#client-table",this.el).append(element); - if (Math.ceil((index + 1) / 10) != 1) { - $(element).hide(); - } + this.addView(client.get('id'), view); }, this); this.togglePlaceholder(); - - }, + views:{}, + + addView:function(index, view) { + this.views[index] = view; + }, + + getView:function(index) { + return this.views[index]; + }, + + getStat:function(index) { + if (!this.stats[index]) { + this.stats[index] = new ClientStatsModel({id: index}); + } + return this.stats[index]; + }, + togglePlaceholder:function() { // set up pagination var numPages = Math.ceil(this.filteredModel.length / 10); @@ -508,6 +543,7 @@ var ClientListView = Backbone.View.extend({ } if (this.filteredModel.length > 0) { + this.changePage(undefined, 1); $('#client-table', this.el).show(); $('#client-table-empty', this.el).hide(); $('#client-table-search-empty', this.el).hide(); @@ -527,14 +563,54 @@ var ClientListView = Backbone.View.extend({ }, changePage:function(event, num) { + console.log('Page changed: ' + num); + $('.paginator', this.el).bootpag({page:num}); + var _self = this; + + _.each(this.filteredModel.models, function (client, index) { + var view = _self.getView(client.get('id')); + if (!view) { + console.log('Error: no view for client ' + client.get('id')); + return; + } + + // only show/render clients on the current page + + console.log(':: ' + index + ' ' + num + ' ' + Math.ceil((index + 1) / 10) != num); + + if (Math.ceil((index + 1) / 10) != num) { + $(view.el).hide(); + } else { + if (!view.isRendered) { + view.render(); + var clientStat = view.options.clientStat; + + // load and display the stats + $.when(clientStat.fetchIfNeeded({ + success:function(e) { + + }, + error:app.errorHandlerView.handleError()})) + .done(function(e) { + view.updateStats(); + }); + } + $(view.el).show(); + } + }); + + /* $('#client-table tbody tr', this.el).each(function(index, element) { if (Math.ceil((index + 1) / 10) != num) { + // hide the element $(element).hide(); } else { + // show the element $(element).show(); } }); + */ }, refreshTable:function(e) { @@ -543,14 +619,12 @@ var ClientListView = Backbone.View.extend({ $('#loading').html( '' + $.t("common.clients") + ' ' + '' + $.t("whitelist.whitelist") + ' ' + - '' + $.t("common.scopes") + ' ' + - '' + $.t("common.statistics") + ' ' + '' + $.t("common.scopes") + ' ' ); var _self = this; $.when(this.model.fetch({success:function(e) {$('#loading-clients').addClass('label-success');}, error:app.errorHandlerView.handleError()}), this.options.whiteListList.fetch({success:function(e) {$('#loading-whitelist').addClass('label-success');}, error:app.errorHandlerView.handleError()}), - this.options.stats.fetch({success:function(e) {$('#loading-stats').addClass('label-success');}, error:app.errorHandlerView.handleError()}), this.options.systemScopeList.fetch({success:function(e) {$('#loading-scopes').addClass('label-success');}, error:app.errorHandlerView.handleError()})) .done(function() { $('#loadingbox').sheet('hide'); @@ -1210,8 +1284,7 @@ ui.routes.push({path: "admin/clients", name: "listClients", callback: this.updateSidebar('admin/clients'); - var view = new ClientListView({model:this.clientList, stats: this.clientStats, systemScopeList: this.systemScopeList, whiteListList: this.whiteListList}); - + var view = new ClientListView({model:this.clientList, systemScopeList: this.systemScopeList, whiteListList: this.whiteListList}); view.load(function() { $('#content').html(view.render().el); view.delegateEvents(); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/template/client.html b/openid-connect-server-webapp/src/main/webapp/resources/template/client.html index b2de62da99..0ee69eb580 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/template/client.html +++ b/openid-connect-server-webapp/src/main/webapp/resources/template/client.html @@ -17,14 +17,8 @@ + + \ No newline at end of file diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultStatsService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultStatsService.java index 26cc34571a..098596e3df 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultStatsService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultStatsService.java @@ -29,6 +29,7 @@ import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.service.ClientDetailsEntityService; import org.mitre.openid.connect.model.ApprovedSite; +import org.mitre.openid.connect.model.ClientStat; import org.mitre.openid.connect.service.ApprovedSiteService; import org.mitre.openid.connect.service.StatsService; import org.springframework.beans.factory.annotation.Autowired; @@ -65,12 +66,12 @@ public Map get() { }, 10, TimeUnit.MINUTES); } - private Supplier> byClientIdCache = createByClientIdCache(); + private Supplier> byClientIdCache = createByClientIdCache(); - private Supplier> createByClientIdCache() { - return Suppliers.memoizeWithExpiration(new Supplier>() { + private Supplier> createByClientIdCache() { + return Suppliers.memoizeWithExpiration(new Supplier>() { @Override - public Map get() { + public Map get() { return computeByClientId(); } @@ -107,11 +108,11 @@ private Map computeSummaryStats() { * @see org.mitre.openid.connect.service.StatsService#calculateByClientId() */ @Override - public Map getByClientId() { + public Map getByClientId() { return byClientIdCache.get(); } - private Map computeByClientId() { + private Map computeByClientId() { // get all approved sites Collection allSites = approvedSiteService.getAll(); @@ -120,10 +121,10 @@ private Map computeByClientId() { clientIds.add(approvedSite.getClientId()); } - Map counts = getEmptyClientCountMap(); + Map counts = getEmptyClientCountMap(); for (String clientId : clientIds) { ClientDetailsEntity client = clientService.loadClientByClientId(clientId); - counts.put(client.getId(), clientIds.count(clientId)); + counts.put(client.getClientId(), clientIds.count(clientId)); } return counts; @@ -133,22 +134,24 @@ private Map computeByClientId() { * @see org.mitre.openid.connect.service.StatsService#countForClientId(java.lang.String) */ @Override - public Integer getCountForClientId(Long id) { - - Map counts = getByClientId(); - return counts.get(id); + public ClientStat getCountForClientId(String id) { + Map counts = getByClientId(); + ClientStat stat = new ClientStat(); + stat.setApprovedSiteCount(counts.get(id)); + + return stat; } /** * Create a new map of all client ids set to zero * @return */ - private Map getEmptyClientCountMap() { - Map counts = new HashMap<>(); + private Map getEmptyClientCountMap() { + Map counts = new HashMap<>(); Collection clients = clientService.getAllClients(); for (ClientDetailsEntity client : clients) { - counts.put(client.getId(), 0); + counts.put(client.getClientId(), 0); } return counts; diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/StatsAPI.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/StatsAPI.java index 2ac2ac51d4..7bbb44c711 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/StatsAPI.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/StatsAPI.java @@ -18,6 +18,7 @@ import java.util.Map; +import org.mitre.openid.connect.model.ClientStat; import org.mitre.openid.connect.service.StatsService; import org.mitre.openid.connect.view.JsonEntityView; import org.slf4j.Logger; @@ -53,20 +54,20 @@ public String statsSummary(ModelMap m) { } - @PreAuthorize("hasRole('ROLE_USER')") - @RequestMapping(value = "byclientid", produces = MediaType.APPLICATION_JSON_VALUE) - public String statsByClient(ModelMap m) { - Map e = statsService.getByClientId(); - - m.put(JsonEntityView.ENTITY, e); - - return JsonEntityView.VIEWNAME; - } - +// @PreAuthorize("hasRole('ROLE_USER')") +// @RequestMapping(value = "byclientid", produces = MediaType.APPLICATION_JSON_VALUE) +// public String statsByClient(ModelMap m) { +// Map e = statsService.getByClientId(); +// +// m.put(JsonEntityView.ENTITY, e); +// +// return JsonEntityView.VIEWNAME; +// } +// @PreAuthorize("hasRole('ROLE_USER')") @RequestMapping(value = "byclientid/{id}", produces = MediaType.APPLICATION_JSON_VALUE) - public String statsByClientId(@PathVariable("id") Long id, ModelMap m) { - Integer e = statsService.getCountForClientId(id); + public String statsByClientId(@PathVariable("id") String clientId, ModelMap m) { + ClientStat e = statsService.getCountForClientId(clientId); m.put(JsonEntityView.ENTITY, e); From a926a8f0abf0b5de6528c8c893c2083e2fc1336f Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 16 Mar 2017 17:31:26 -0400 Subject: [PATCH 161/269] cleaned up server-side stats service, UI now uses per-client calls --- .../openid/connect/service/StatsService.java | 7 --- .../service/impl/DefaultStatsService.java | 63 ++----------------- 2 files changed, 4 insertions(+), 66 deletions(-) diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/StatsService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/StatsService.java index 8b2be24bb3..502094b603 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/StatsService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/StatsService.java @@ -39,13 +39,6 @@ public interface StatsService { */ public Map getSummaryStats(); - /** - * Calculate usage count for all clients - * - * @return a map of id of client object to number of approvals - */ - public Map getByClientId(); - /** * Calculate the usage count for a single client * diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultStatsService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultStatsService.java index 098596e3df..c22c1dd58d 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultStatsService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultStatsService.java @@ -50,9 +50,6 @@ public class DefaultStatsService implements StatsService { @Autowired private ApprovedSiteService approvedSiteService; - @Autowired - private ClientDetailsEntityService clientService; - // stats cache private Supplier> summaryCache = createSummaryCache(); @@ -66,18 +63,6 @@ public Map get() { }, 10, TimeUnit.MINUTES); } - private Supplier> byClientIdCache = createByClientIdCache(); - - private Supplier> createByClientIdCache() { - return Suppliers.memoizeWithExpiration(new Supplier>() { - @Override - public Map get() { - return computeByClientId(); - } - - }, 10, TimeUnit.MINUTES); - } - @Override public Map getSummaryStats() { return summaryCache.get(); @@ -104,66 +89,26 @@ private Map computeSummaryStats() { return e; } - /* (non-Javadoc) - * @see org.mitre.openid.connect.service.StatsService#calculateByClientId() - */ - @Override - public Map getByClientId() { - return byClientIdCache.get(); - } - - private Map computeByClientId() { - // get all approved sites - Collection allSites = approvedSiteService.getAll(); - - Multiset clientIds = HashMultiset.create(); - for (ApprovedSite approvedSite : allSites) { - clientIds.add(approvedSite.getClientId()); - } - - Map counts = getEmptyClientCountMap(); - for (String clientId : clientIds) { - ClientDetailsEntity client = clientService.loadClientByClientId(clientId); - counts.put(client.getClientId(), clientIds.count(clientId)); - } - - return counts; - } - /* (non-Javadoc) * @see org.mitre.openid.connect.service.StatsService#countForClientId(java.lang.String) */ @Override - public ClientStat getCountForClientId(String id) { + public ClientStat getCountForClientId(String clientId) { - Map counts = getByClientId(); + Collection approvedSites = approvedSiteService.getByClientId(clientId); + ClientStat stat = new ClientStat(); - stat.setApprovedSiteCount(counts.get(id)); + stat.setApprovedSiteCount(approvedSites.size()); return stat; } - /** - * Create a new map of all client ids set to zero - * @return - */ - private Map getEmptyClientCountMap() { - Map counts = new HashMap<>(); - Collection clients = clientService.getAllClients(); - for (ClientDetailsEntity client : clients) { - counts.put(client.getClientId(), 0); - } - - return counts; - } - /** * Reset both stats caches on a trigger (before the timer runs out). Resets the timers. */ @Override public void resetCache() { summaryCache = createSummaryCache(); - byClientIdCache = createByClientIdCache(); } } From 2a75535dce1f94240a333aea13d15e51fc607315 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 16 Mar 2017 17:55:31 -0400 Subject: [PATCH 162/269] fix unit tests and downstream calls --- .../web/OAuthConfirmationController.java | 2 +- .../service/impl/TestDefaultStatsService.java | 48 ++++--------------- 2 files changed, 11 insertions(+), 39 deletions(-) diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuthConfirmationController.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuthConfirmationController.java index 4e0ca9c142..39d356c85d 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuthConfirmationController.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuthConfirmationController.java @@ -201,7 +201,7 @@ public String confimAccess(Map model, @ModelAttribute("authoriza model.put("claims", claimsForScopes); // client stats - Integer count = statsService.getCountForClientId(client.getId()); + Integer count = statsService.getCountForClientId(client.getClientId()).getApprovedSiteCount(); model.put("count", count); diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultStatsService.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultStatsService.java index fb8ae78f99..930e659558 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultStatsService.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultStatsService.java @@ -71,9 +71,6 @@ public class TestDefaultStatsService { @Mock private ApprovedSiteService approvedSiteService; - @Mock - private ClientDetailsEntityService clientService; - @InjectMocks private DefaultStatsService service = new DefaultStatsService(); @@ -84,7 +81,7 @@ public class TestDefaultStatsService { @Before public void prepare() { - Mockito.reset(approvedSiteService, clientService); + Mockito.reset(approvedSiteService); Mockito.when(ap1.getUserId()).thenReturn(userId1); Mockito.when(ap1.getClientId()).thenReturn(clientId1); @@ -111,11 +108,10 @@ public void prepare() { Mockito.when(client3.getId()).thenReturn(3L); Mockito.when(client4.getId()).thenReturn(4L); - Mockito.when(clientService.getAllClients()).thenReturn(Sets.newHashSet(client1, client2, client3, client4)); - Mockito.when(clientService.loadClientByClientId(clientId1)).thenReturn(client1); - Mockito.when(clientService.loadClientByClientId(clientId2)).thenReturn(client2); - Mockito.when(clientService.loadClientByClientId(clientId3)).thenReturn(client3); - Mockito.when(clientService.loadClientByClientId(clientId4)).thenReturn(client4); + Mockito.when(approvedSiteService.getByClientId(clientId1)).thenReturn(Sets.newHashSet(ap1, ap2)); + Mockito.when(approvedSiteService.getByClientId(clientId2)).thenReturn(Sets.newHashSet(ap3)); + Mockito.when(approvedSiteService.getByClientId(clientId3)).thenReturn(Sets.newHashSet(ap4)); + Mockito.when(approvedSiteService.getByClientId(clientId4)).thenReturn(Sets.newHashSet()); } @Test @@ -139,37 +135,13 @@ public void calculateSummaryStats() { assertThat(stats.get("clientCount"), is(3)); } - @Test - public void calculateByClientId_empty() { - - Mockito.when(approvedSiteService.getAll()).thenReturn(new HashSet()); - - Map stats = service.getByClientId(); - - assertThat(stats.get(1L), is(0)); - assertThat(stats.get(2L), is(0)); - assertThat(stats.get(3L), is(0)); - assertThat(stats.get(4L), is(0)); - } - - @Test - public void calculateByClientId() { - - Map stats = service.getByClientId(); - - assertThat(stats.get(1L), is(2)); - assertThat(stats.get(2L), is(1)); - assertThat(stats.get(3L), is(1)); - assertThat(stats.get(4L), is(0)); - } - @Test public void countForClientId() { - - assertThat(service.getCountForClientId(1L), is(2)); - assertThat(service.getCountForClientId(2L), is(1)); - assertThat(service.getCountForClientId(3L), is(1)); - assertThat(service.getCountForClientId(4L), is(0)); + // stats for ap1..ap4 + assertThat(service.getCountForClientId(clientId1).getApprovedSiteCount(), is(2)); + assertThat(service.getCountForClientId(clientId2).getApprovedSiteCount(), is(1)); + assertThat(service.getCountForClientId(clientId3).getApprovedSiteCount(), is(1)); + assertThat(service.getCountForClientId(clientId4).getApprovedSiteCount(), is(0)); } @Test From c377e411e390ad13ea4fd081d538751a583b808b Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 17 Mar 2017 16:32:41 -0400 Subject: [PATCH 163/269] removed packed-in bootstrap files (again) --- .../bootstrap2/css/bootstrap-responsive.css | 1089 ---- .../resources/bootstrap2/css/bootstrap.css | 5236 ----------------- 2 files changed, 6325 deletions(-) delete mode 100644 openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.css delete mode 100644 openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.css diff --git a/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.css b/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.css deleted file mode 100644 index ac63813ee1..0000000000 --- a/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.css +++ /dev/null @@ -1,1089 +0,0 @@ -/*! - * Bootstrap Responsive v2.3.2 - * - * Copyright 2013 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world by @mdo and @fat. - */ -.clearfix { - *zoom: 1; -} -.clearfix:before, -.clearfix:after { - display: table; - content: ""; - line-height: 0; -} -.clearfix:after { - clear: both; -} -.hide-text { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} -.input-block-level { - display: block; - width: 100%; - min-height: 30px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -@-ms-viewport { - width: device-width; -} -.hidden { - display: none; - visibility: hidden; -} -.visible-phone { - display: none !important; -} -.visible-tablet { - display: none !important; -} -.hidden-desktop { - display: none !important; -} -.visible-desktop { - display: inherit !important; -} -@media (min-width: 768px) and (max-width: 979px) { - .hidden-desktop { - display: inherit !important; - } - .visible-desktop { - display: none !important ; - } - .visible-tablet { - display: inherit !important; - } - .hidden-tablet { - display: none !important; - } -} -@media (max-width: 767px) { - .hidden-desktop { - display: inherit !important; - } - .visible-desktop { - display: none !important; - } - .visible-phone { - display: inherit !important; - } - .hidden-phone { - display: none !important; - } -} -.visible-print { - display: none !important; -} -@media print { - .visible-print { - display: inherit !important; - } - .hidden-print { - display: none !important; - } -} -@media (min-width: 1200px) { - .row { - margin-left: -30px; - *zoom: 1; - } - .row:before, - .row:after { - display: table; - content: ""; - line-height: 0; - } - .row:after { - clear: both; - } - [class*="span"] { - float: left; - min-height: 1px; - margin-left: 30px; - } - .container, - .navbar-static-top .container, - .navbar-fixed-top .container, - .navbar-fixed-bottom .container { - width: 1170px; - } - .span12 { - width: 1170px; - } - .span11 { - width: 1070px; - } - .span10 { - width: 970px; - } - .span9 { - width: 870px; - } - .span8 { - width: 770px; - } - .span7 { - width: 670px; - } - .span6 { - width: 570px; - } - .span5 { - width: 470px; - } - .span4 { - width: 370px; - } - .span3 { - width: 270px; - } - .span2 { - width: 170px; - } - .span1 { - width: 70px; - } - .offset12 { - margin-left: 1230px; - } - .offset11 { - margin-left: 1130px; - } - .offset10 { - margin-left: 1030px; - } - .offset9 { - margin-left: 930px; - } - .offset8 { - margin-left: 830px; - } - .offset7 { - margin-left: 730px; - } - .offset6 { - margin-left: 630px; - } - .offset5 { - margin-left: 530px; - } - .offset4 { - margin-left: 430px; - } - .offset3 { - margin-left: 330px; - } - .offset2 { - margin-left: 230px; - } - .offset1 { - margin-left: 130px; - } - .row-fluid { - width: 100%; - *zoom: 1; - } - .row-fluid:before, - .row-fluid:after { - display: table; - content: ""; - line-height: 0; - } - .row-fluid:after { - clear: both; - } - .row-fluid [class*="span"] { - display: block; - width: 100%; - min-height: 30px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - float: left; - margin-left: 2.56410256%; - *margin-left: 2.51091107%; - } - .row-fluid [class*="span"]:first-child { - margin-left: 0; - } - .row-fluid .controls-row [class*="span"] + [class*="span"] { - margin-left: 2.56410256%; - } - .row-fluid .span12 { - width: 100%; - *width: 99.94680851%; - } - .row-fluid .span11 { - width: 91.45299145%; - *width: 91.39979996%; - } - .row-fluid .span10 { - width: 82.90598291%; - *width: 82.85279142%; - } - .row-fluid .span9 { - width: 74.35897436%; - *width: 74.30578287%; - } - .row-fluid .span8 { - width: 65.81196581%; - *width: 65.75877432%; - } - .row-fluid .span7 { - width: 57.26495726%; - *width: 57.21176578%; - } - .row-fluid .span6 { - width: 48.71794872%; - *width: 48.66475723%; - } - .row-fluid .span5 { - width: 40.17094017%; - *width: 40.11774868%; - } - .row-fluid .span4 { - width: 31.62393162%; - *width: 31.57074013%; - } - .row-fluid .span3 { - width: 23.07692308%; - *width: 23.02373159%; - } - .row-fluid .span2 { - width: 14.52991453%; - *width: 14.47672304%; - } - .row-fluid .span1 { - width: 5.98290598%; - *width: 5.92971449%; - } - .row-fluid .offset12 { - margin-left: 105.12820513%; - *margin-left: 105.02182215%; - } - .row-fluid .offset12:first-child { - margin-left: 102.56410256%; - *margin-left: 102.45771959%; - } - .row-fluid .offset11 { - margin-left: 96.58119658%; - *margin-left: 96.4748136%; - } - .row-fluid .offset11:first-child { - margin-left: 94.01709402%; - *margin-left: 93.91071104%; - } - .row-fluid .offset10 { - margin-left: 88.03418803%; - *margin-left: 87.92780506%; - } - .row-fluid .offset10:first-child { - margin-left: 85.47008547%; - *margin-left: 85.36370249%; - } - .row-fluid .offset9 { - margin-left: 79.48717949%; - *margin-left: 79.38079651%; - } - .row-fluid .offset9:first-child { - margin-left: 76.92307692%; - *margin-left: 76.81669394%; - } - .row-fluid .offset8 { - margin-left: 70.94017094%; - *margin-left: 70.83378796%; - } - .row-fluid .offset8:first-child { - margin-left: 68.37606838%; - *margin-left: 68.2696854%; - } - .row-fluid .offset7 { - margin-left: 62.39316239%; - *margin-left: 62.28677941%; - } - .row-fluid .offset7:first-child { - margin-left: 59.82905983%; - *margin-left: 59.72267685%; - } - .row-fluid .offset6 { - margin-left: 53.84615385%; - *margin-left: 53.73977087%; - } - .row-fluid .offset6:first-child { - margin-left: 51.28205128%; - *margin-left: 51.1756683%; - } - .row-fluid .offset5 { - margin-left: 45.2991453%; - *margin-left: 45.19276232%; - } - .row-fluid .offset5:first-child { - margin-left: 42.73504274%; - *margin-left: 42.62865976%; - } - .row-fluid .offset4 { - margin-left: 36.75213675%; - *margin-left: 36.64575377%; - } - .row-fluid .offset4:first-child { - margin-left: 34.18803419%; - *margin-left: 34.08165121%; - } - .row-fluid .offset3 { - margin-left: 28.20512821%; - *margin-left: 28.09874523%; - } - .row-fluid .offset3:first-child { - margin-left: 25.64102564%; - *margin-left: 25.53464266%; - } - .row-fluid .offset2 { - margin-left: 19.65811966%; - *margin-left: 19.55173668%; - } - .row-fluid .offset2:first-child { - margin-left: 17.09401709%; - *margin-left: 16.98763412%; - } - .row-fluid .offset1 { - margin-left: 11.11111111%; - *margin-left: 11.00472813%; - } - .row-fluid .offset1:first-child { - margin-left: 8.54700855%; - *margin-left: 8.44062557%; - } - input, - textarea, - .uneditable-input { - margin-left: 0; - } - .controls-row [class*="span"] + [class*="span"] { - margin-left: 30px; - } - input.span12, - textarea.span12, - .uneditable-input.span12 { - width: 1156px; - } - input.span11, - textarea.span11, - .uneditable-input.span11 { - width: 1056px; - } - input.span10, - textarea.span10, - .uneditable-input.span10 { - width: 956px; - } - input.span9, - textarea.span9, - .uneditable-input.span9 { - width: 856px; - } - input.span8, - textarea.span8, - .uneditable-input.span8 { - width: 756px; - } - input.span7, - textarea.span7, - .uneditable-input.span7 { - width: 656px; - } - input.span6, - textarea.span6, - .uneditable-input.span6 { - width: 556px; - } - input.span5, - textarea.span5, - .uneditable-input.span5 { - width: 456px; - } - input.span4, - textarea.span4, - .uneditable-input.span4 { - width: 356px; - } - input.span3, - textarea.span3, - .uneditable-input.span3 { - width: 256px; - } - input.span2, - textarea.span2, - .uneditable-input.span2 { - width: 156px; - } - input.span1, - textarea.span1, - .uneditable-input.span1 { - width: 56px; - } - .thumbnails { - margin-left: -30px; - } - .thumbnails > li { - margin-left: 30px; - } - .row-fluid .thumbnails { - margin-left: 0; - } -} -@media (min-width: 768px) and (max-width: 979px) { - .row { - margin-left: -20px; - *zoom: 1; - } - .row:before, - .row:after { - display: table; - content: ""; - line-height: 0; - } - .row:after { - clear: both; - } - [class*="span"] { - float: left; - min-height: 1px; - margin-left: 20px; - } - .container, - .navbar-static-top .container, - .navbar-fixed-top .container, - .navbar-fixed-bottom .container { - width: 724px; - } - .span12 { - width: 724px; - } - .span11 { - width: 662px; - } - .span10 { - width: 600px; - } - .span9 { - width: 538px; - } - .span8 { - width: 476px; - } - .span7 { - width: 414px; - } - .span6 { - width: 352px; - } - .span5 { - width: 290px; - } - .span4 { - width: 228px; - } - .span3 { - width: 166px; - } - .span2 { - width: 104px; - } - .span1 { - width: 42px; - } - .offset12 { - margin-left: 764px; - } - .offset11 { - margin-left: 702px; - } - .offset10 { - margin-left: 640px; - } - .offset9 { - margin-left: 578px; - } - .offset8 { - margin-left: 516px; - } - .offset7 { - margin-left: 454px; - } - .offset6 { - margin-left: 392px; - } - .offset5 { - margin-left: 330px; - } - .offset4 { - margin-left: 268px; - } - .offset3 { - margin-left: 206px; - } - .offset2 { - margin-left: 144px; - } - .offset1 { - margin-left: 82px; - } - .row-fluid { - width: 100%; - *zoom: 1; - } - .row-fluid:before, - .row-fluid:after { - display: table; - content: ""; - line-height: 0; - } - .row-fluid:after { - clear: both; - } - .row-fluid [class*="span"] { - display: block; - width: 100%; - min-height: 30px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - float: left; - margin-left: 2.76243094%; - *margin-left: 2.70923945%; - } - .row-fluid [class*="span"]:first-child { - margin-left: 0; - } - .row-fluid .controls-row [class*="span"] + [class*="span"] { - margin-left: 2.76243094%; - } - .row-fluid .span12 { - width: 100%; - *width: 99.94680851%; - } - .row-fluid .span11 { - width: 91.43646409%; - *width: 91.3832726%; - } - .row-fluid .span10 { - width: 82.87292818%; - *width: 82.81973669%; - } - .row-fluid .span9 { - width: 74.30939227%; - *width: 74.25620078%; - } - .row-fluid .span8 { - width: 65.74585635%; - *width: 65.69266486%; - } - .row-fluid .span7 { - width: 57.18232044%; - *width: 57.12912895%; - } - .row-fluid .span6 { - width: 48.61878453%; - *width: 48.56559304%; - } - .row-fluid .span5 { - width: 40.05524862%; - *width: 40.00205713%; - } - .row-fluid .span4 { - width: 31.49171271%; - *width: 31.43852122%; - } - .row-fluid .span3 { - width: 22.9281768%; - *width: 22.87498531%; - } - .row-fluid .span2 { - width: 14.36464088%; - *width: 14.31144939%; - } - .row-fluid .span1 { - width: 5.80110497%; - *width: 5.74791348%; - } - .row-fluid .offset12 { - margin-left: 105.52486188%; - *margin-left: 105.4184789%; - } - .row-fluid .offset12:first-child { - margin-left: 102.76243094%; - *margin-left: 102.65604796%; - } - .row-fluid .offset11 { - margin-left: 96.96132597%; - *margin-left: 96.85494299%; - } - .row-fluid .offset11:first-child { - margin-left: 94.19889503%; - *margin-left: 94.09251205%; - } - .row-fluid .offset10 { - margin-left: 88.39779006%; - *margin-left: 88.29140708%; - } - .row-fluid .offset10:first-child { - margin-left: 85.63535912%; - *margin-left: 85.52897614%; - } - .row-fluid .offset9 { - margin-left: 79.83425414%; - *margin-left: 79.72787116%; - } - .row-fluid .offset9:first-child { - margin-left: 77.0718232%; - *margin-left: 76.96544023%; - } - .row-fluid .offset8 { - margin-left: 71.27071823%; - *margin-left: 71.16433525%; - } - .row-fluid .offset8:first-child { - margin-left: 68.50828729%; - *margin-left: 68.40190431%; - } - .row-fluid .offset7 { - margin-left: 62.70718232%; - *margin-left: 62.60079934%; - } - .row-fluid .offset7:first-child { - margin-left: 59.94475138%; - *margin-left: 59.8383684%; - } - .row-fluid .offset6 { - margin-left: 54.14364641%; - *margin-left: 54.03726343%; - } - .row-fluid .offset6:first-child { - margin-left: 51.38121547%; - *margin-left: 51.27483249%; - } - .row-fluid .offset5 { - margin-left: 45.5801105%; - *margin-left: 45.47372752%; - } - .row-fluid .offset5:first-child { - margin-left: 42.81767956%; - *margin-left: 42.71129658%; - } - .row-fluid .offset4 { - margin-left: 37.01657459%; - *margin-left: 36.91019161%; - } - .row-fluid .offset4:first-child { - margin-left: 34.25414365%; - *margin-left: 34.14776067%; - } - .row-fluid .offset3 { - margin-left: 28.45303867%; - *margin-left: 28.3466557%; - } - .row-fluid .offset3:first-child { - margin-left: 25.69060773%; - *margin-left: 25.58422476%; - } - .row-fluid .offset2 { - margin-left: 19.88950276%; - *margin-left: 19.78311978%; - } - .row-fluid .offset2:first-child { - margin-left: 17.12707182%; - *margin-left: 17.02068884%; - } - .row-fluid .offset1 { - margin-left: 11.32596685%; - *margin-left: 11.21958387%; - } - .row-fluid .offset1:first-child { - margin-left: 8.56353591%; - *margin-left: 8.45715293%; - } - input, - textarea, - .uneditable-input { - margin-left: 0; - } - .controls-row [class*="span"] + [class*="span"] { - margin-left: 20px; - } - input.span12, - textarea.span12, - .uneditable-input.span12 { - width: 710px; - } - input.span11, - textarea.span11, - .uneditable-input.span11 { - width: 648px; - } - input.span10, - textarea.span10, - .uneditable-input.span10 { - width: 586px; - } - input.span9, - textarea.span9, - .uneditable-input.span9 { - width: 524px; - } - input.span8, - textarea.span8, - .uneditable-input.span8 { - width: 462px; - } - input.span7, - textarea.span7, - .uneditable-input.span7 { - width: 400px; - } - input.span6, - textarea.span6, - .uneditable-input.span6 { - width: 338px; - } - input.span5, - textarea.span5, - .uneditable-input.span5 { - width: 276px; - } - input.span4, - textarea.span4, - .uneditable-input.span4 { - width: 214px; - } - input.span3, - textarea.span3, - .uneditable-input.span3 { - width: 152px; - } - input.span2, - textarea.span2, - .uneditable-input.span2 { - width: 90px; - } - input.span1, - textarea.span1, - .uneditable-input.span1 { - width: 28px; - } -} -@media (max-width: 767px) { - body { - padding-left: 20px; - padding-right: 20px; - } - .navbar-fixed-top, - .navbar-fixed-bottom, - .navbar-static-top { - margin-left: -20px; - margin-right: -20px; - } - .container-fluid { - padding: 0; - } - .dl-horizontal dt { - float: none; - clear: none; - width: auto; - text-align: left; - } - .dl-horizontal dd { - margin-left: 0; - } - .container { - width: auto; - } - .row-fluid { - width: 100%; - } - .row, - .thumbnails { - margin-left: 0; - } - .thumbnails > li { - float: none; - margin-left: 0; - } - [class*="span"], - .uneditable-input[class*="span"], - .row-fluid [class*="span"] { - float: none; - display: block; - width: 100%; - margin-left: 0; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - } - .span12, - .row-fluid .span12 { - width: 100%; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - } - .row-fluid [class*="offset"]:first-child { - margin-left: 0; - } - .input-large, - .input-xlarge, - .input-xxlarge, - input[class*="span"], - select[class*="span"], - textarea[class*="span"], - .uneditable-input { - display: block; - width: 100%; - min-height: 30px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - } - .input-prepend input, - .input-append input, - .input-prepend input[class*="span"], - .input-append input[class*="span"] { - display: inline-block; - width: auto; - } - .controls-row [class*="span"] + [class*="span"] { - margin-left: 0; - } - .modal { - position: fixed; - top: 20px; - left: 20px; - right: 20px; - width: auto; - margin: 0; - } - .modal.fade { - top: -100px; - } - .modal.fade.in { - top: 20px; - } -} -@media (max-width: 480px) { - .nav-collapse { - -webkit-transform: translate3d(0, 0, 0); - } - .page-header h1 small { - display: block; - line-height: 20px; - } - input[type="checkbox"], - input[type="radio"] { - border: 1px solid #ccc; - } - .form-horizontal .control-label { - float: none; - width: auto; - padding-top: 0; - text-align: left; - } - .form-horizontal .controls { - margin-left: 0; - } - .form-horizontal .control-list { - padding-top: 0; - } - .form-horizontal .form-actions { - padding-left: 10px; - padding-right: 10px; - } - .media .pull-left, - .media .pull-right { - float: none; - display: block; - margin-bottom: 10px; - } - .media-object { - margin-right: 0; - margin-left: 0; - } - .modal { - top: 10px; - left: 10px; - right: 10px; - } - .modal-header .close { - padding: 10px; - margin: -10px; - } - .carousel-caption { - position: static; - } -} -@media (max-width: 979px) { - body { - padding-top: 0; - } - .navbar-fixed-top, - .navbar-fixed-bottom { - position: static; - } - .navbar-fixed-top { - margin-bottom: 20px; - } - .navbar-fixed-bottom { - margin-top: 20px; - } - .navbar-fixed-top .navbar-inner, - .navbar-fixed-bottom .navbar-inner { - padding: 5px; - } - .navbar .container { - width: auto; - padding: 0; - } - .navbar .brand { - padding-left: 10px; - padding-right: 10px; - margin: 0 0 0 -5px; - } - .nav-collapse { - clear: both; - } - .nav-collapse .nav { - float: none; - margin: 0 0 10px; - } - .nav-collapse .nav > li { - float: none; - } - .nav-collapse .nav > li > a { - margin-bottom: 2px; - } - .nav-collapse .nav > .divider-vertical { - display: none; - } - .nav-collapse .nav .nav-header { - color: #777777; - text-shadow: none; - } - .nav-collapse .nav > li > a, - .nav-collapse .dropdown-menu a { - padding: 9px 15px; - font-weight: bold; - color: #777777; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - } - .nav-collapse .btn { - padding: 4px 10px 4px; - font-weight: normal; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - } - .nav-collapse .dropdown-menu li + li a { - margin-bottom: 2px; - } - .nav-collapse .nav > li > a:hover, - .nav-collapse .nav > li > a:focus, - .nav-collapse .dropdown-menu a:hover, - .nav-collapse .dropdown-menu a:focus { - background-color: #f2f2f2; - } - .navbar-inverse .nav-collapse .nav > li > a, - .navbar-inverse .nav-collapse .dropdown-menu a { - color: #999999; - } - .navbar-inverse .nav-collapse .nav > li > a:hover, - .navbar-inverse .nav-collapse .nav > li > a:focus, - .navbar-inverse .nav-collapse .dropdown-menu a:hover, - .navbar-inverse .nav-collapse .dropdown-menu a:focus { - background-color: #111111; - } - .nav-collapse.in .btn-group { - margin-top: 5px; - padding: 0; - } - .nav-collapse .dropdown-menu { - position: static; - top: auto; - left: auto; - float: none; - display: none; - max-width: none; - margin: 0 15px; - padding: 0; - background-color: transparent; - border: none; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - } - .nav-collapse .open > .dropdown-menu { - display: block; - } - .nav-collapse .dropdown-menu:before, - .nav-collapse .dropdown-menu:after { - display: none; - } - .nav-collapse .dropdown-menu .divider { - display: none; - } - .nav-collapse .nav > li > .dropdown-menu:before, - .nav-collapse .nav > li > .dropdown-menu:after { - display: none; - } - .nav-collapse .navbar-form, - .nav-collapse .navbar-search { - float: none; - padding: 10px 15px; - margin: 10px 0; - border-top: 1px solid #f2f2f2; - border-bottom: 1px solid #f2f2f2; - -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1); - -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1); - box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1); - } - .navbar-inverse .nav-collapse .navbar-form, - .navbar-inverse .nav-collapse .navbar-search { - border-top-color: #111111; - border-bottom-color: #111111; - } - .navbar .nav-collapse .nav.pull-right { - float: none; - margin-left: 0; - } - .nav-collapse, - .nav-collapse.collapse { - overflow: hidden; - height: 0; - } - .navbar .btn-navbar { - display: block; - } - .navbar-static .navbar-inner { - padding-left: 10px; - padding-right: 10px; - } -} -@media (min-width: 979px + 1) { - .nav-collapse.collapse { - height: auto !important; - overflow: visible !important; - } -} - diff --git a/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.css b/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.css deleted file mode 100644 index dd320d684b..0000000000 --- a/openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.css +++ /dev/null @@ -1,5236 +0,0 @@ -/*! - * Bootstrap v2.3.2 - * - * Copyright 2013 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world by @mdo and @fat. - */ -.clearfix { - *zoom: 1; -} -.clearfix:before, -.clearfix:after { - display: table; - content: ""; - line-height: 0; -} -.clearfix:after { - clear: both; -} -.hide-text { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} -.input-block-level { - display: block; - width: 100%; - min-height: 30px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -nav, -section { - display: block; -} -audio, -canvas, -video { - display: inline-block; - *display: inline; - *zoom: 1; -} -audio:not([controls]) { - display: none; -} -html { - font-size: 100%; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} -a:focus { - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -a:hover, -a:active { - outline: 0; -} -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} -sup { - top: -0.5em; -} -sub { - bottom: -0.25em; -} -img { - /* Responsive images (ensure images don't scale beyond their parents) */ - max-width: 100%; - /* Part 1: Set a maxium relative to the parent */ - width: auto\9; - /* IE7-8 need help adjusting responsive images */ - height: auto; - /* Part 2: Scale the height according to the width, otherwise you get stretching */ - vertical-align: middle; - border: 0; - -ms-interpolation-mode: bicubic; -} -#map_canvas img, -.google-maps img { - max-width: none; -} -button, -input, -select, -textarea { - margin: 0; - font-size: 100%; - vertical-align: middle; -} -button, -input { - *overflow: visible; - line-height: normal; -} -button::-moz-focus-inner, -input::-moz-focus-inner { - padding: 0; - border: 0; -} -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; - cursor: pointer; -} -label, -select, -button, -input[type="button"], -input[type="reset"], -input[type="submit"], -input[type="radio"], -input[type="checkbox"] { - cursor: pointer; -} -input[type="search"] { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; - -webkit-appearance: textfield; -} -input[type="search"]::-webkit-search-decoration, -input[type="search"]::-webkit-search-cancel-button { - -webkit-appearance: none; -} -textarea { - overflow: auto; - vertical-align: top; -} -@media print { - * { - text-shadow: none !important; - color: #000 !important; - background: transparent !important; - box-shadow: none !important; - } - a, - a:visited { - text-decoration: underline; - } - a[href]:after { - content: " (" attr(href) ")"; - } - abbr[title]:after { - content: " (" attr(title) ")"; - } - .ir a:after, - a[href^="javascript:"]:after, - a[href^="#"]:after { - content: ""; - } - pre, - blockquote { - border: 1px solid #999; - page-break-inside: avoid; - } - thead { - display: table-header-group; - } - tr, - img { - page-break-inside: avoid; - } - img { - max-width: 100% !important; - } - @page { - margin: 0.5cm; - } - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - h2, - h3 { - page-break-after: avoid; - } -} -body { - margin: 0; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 20px; - color: #333333; - background-color: #ffffff; -} -a { - color: #0088cc; - text-decoration: none; -} -a:hover, -a:focus { - color: #005580; - text-decoration: underline; -} -.img-rounded { - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} -.img-polaroid { - padding: 4px; - background-color: #fff; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); -} -.img-circle { - -webkit-border-radius: 500px; - -moz-border-radius: 500px; - border-radius: 500px; -} -.row { - margin-left: -20px; - *zoom: 1; -} -.row:before, -.row:after { - display: table; - content: ""; - line-height: 0; -} -.row:after { - clear: both; -} -[class*="span"] { - float: left; - min-height: 1px; - margin-left: 20px; -} -.container, -.navbar-static-top .container, -.navbar-fixed-top .container, -.navbar-fixed-bottom .container { - width: 940px; -} -.span12 { - width: 940px; -} -.span11 { - width: 860px; -} -.span10 { - width: 780px; -} -.span9 { - width: 700px; -} -.span8 { - width: 620px; -} -.span7 { - width: 540px; -} -.span6 { - width: 460px; -} -.span5 { - width: 380px; -} -.span4 { - width: 300px; -} -.span3 { - width: 220px; -} -.span2 { - width: 140px; -} -.span1 { - width: 60px; -} -.offset12 { - margin-left: 980px; -} -.offset11 { - margin-left: 900px; -} -.offset10 { - margin-left: 820px; -} -.offset9 { - margin-left: 740px; -} -.offset8 { - margin-left: 660px; -} -.offset7 { - margin-left: 580px; -} -.offset6 { - margin-left: 500px; -} -.offset5 { - margin-left: 420px; -} -.offset4 { - margin-left: 340px; -} -.offset3 { - margin-left: 260px; -} -.offset2 { - margin-left: 180px; -} -.offset1 { - margin-left: 100px; -} -.row-fluid { - width: 100%; - *zoom: 1; -} -.row-fluid:before, -.row-fluid:after { - display: table; - content: ""; - line-height: 0; -} -.row-fluid:after { - clear: both; -} -.row-fluid [class*="span"] { - display: block; - width: 100%; - min-height: 30px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - float: left; - margin-left: 2.12765957%; - *margin-left: 2.07446809%; -} -.row-fluid [class*="span"]:first-child { - margin-left: 0; -} -.row-fluid .controls-row [class*="span"] + [class*="span"] { - margin-left: 2.12765957%; -} -.row-fluid .span12 { - width: 100%; - *width: 99.94680851%; -} -.row-fluid .span11 { - width: 91.4893617%; - *width: 91.43617021%; -} -.row-fluid .span10 { - width: 82.9787234%; - *width: 82.92553191%; -} -.row-fluid .span9 { - width: 74.46808511%; - *width: 74.41489362%; -} -.row-fluid .span8 { - width: 65.95744681%; - *width: 65.90425532%; -} -.row-fluid .span7 { - width: 57.44680851%; - *width: 57.39361702%; -} -.row-fluid .span6 { - width: 48.93617021%; - *width: 48.88297872%; -} -.row-fluid .span5 { - width: 40.42553191%; - *width: 40.37234043%; -} -.row-fluid .span4 { - width: 31.91489362%; - *width: 31.86170213%; -} -.row-fluid .span3 { - width: 23.40425532%; - *width: 23.35106383%; -} -.row-fluid .span2 { - width: 14.89361702%; - *width: 14.84042553%; -} -.row-fluid .span1 { - width: 6.38297872%; - *width: 6.32978723%; -} -.row-fluid .offset12 { - margin-left: 104.25531915%; - *margin-left: 104.14893617%; -} -.row-fluid .offset12:first-child { - margin-left: 102.12765957%; - *margin-left: 102.0212766%; -} -.row-fluid .offset11 { - margin-left: 95.74468085%; - *margin-left: 95.63829787%; -} -.row-fluid .offset11:first-child { - margin-left: 93.61702128%; - *margin-left: 93.5106383%; -} -.row-fluid .offset10 { - margin-left: 87.23404255%; - *margin-left: 87.12765957%; -} -.row-fluid .offset10:first-child { - margin-left: 85.10638298%; - *margin-left: 85%; -} -.row-fluid .offset9 { - margin-left: 78.72340426%; - *margin-left: 78.61702128%; -} -.row-fluid .offset9:first-child { - margin-left: 76.59574468%; - *margin-left: 76.4893617%; -} -.row-fluid .offset8 { - margin-left: 70.21276596%; - *margin-left: 70.10638298%; -} -.row-fluid .offset8:first-child { - margin-left: 68.08510638%; - *margin-left: 67.9787234%; -} -.row-fluid .offset7 { - margin-left: 61.70212766%; - *margin-left: 61.59574468%; -} -.row-fluid .offset7:first-child { - margin-left: 59.57446809%; - *margin-left: 59.46808511%; -} -.row-fluid .offset6 { - margin-left: 53.19148936%; - *margin-left: 53.08510638%; -} -.row-fluid .offset6:first-child { - margin-left: 51.06382979%; - *margin-left: 50.95744681%; -} -.row-fluid .offset5 { - margin-left: 44.68085106%; - *margin-left: 44.57446809%; -} -.row-fluid .offset5:first-child { - margin-left: 42.55319149%; - *margin-left: 42.44680851%; -} -.row-fluid .offset4 { - margin-left: 36.17021277%; - *margin-left: 36.06382979%; -} -.row-fluid .offset4:first-child { - margin-left: 34.04255319%; - *margin-left: 33.93617021%; -} -.row-fluid .offset3 { - margin-left: 27.65957447%; - *margin-left: 27.55319149%; -} -.row-fluid .offset3:first-child { - margin-left: 25.53191489%; - *margin-left: 25.42553191%; -} -.row-fluid .offset2 { - margin-left: 19.14893617%; - *margin-left: 19.04255319%; -} -.row-fluid .offset2:first-child { - margin-left: 17.0212766%; - *margin-left: 16.91489362%; -} -.row-fluid .offset1 { - margin-left: 10.63829787%; - *margin-left: 10.53191489%; -} -.row-fluid .offset1:first-child { - margin-left: 8.5106383%; - *margin-left: 8.40425532%; -} -[class*="span"].hide, -.row-fluid [class*="span"].hide { - display: none; -} -[class*="span"].pull-right, -.row-fluid [class*="span"].pull-right { - float: right; -} -.container { - margin-right: auto; - margin-left: auto; - *zoom: 1; -} -.container:before, -.container:after { - display: table; - content: ""; - line-height: 0; -} -.container:after { - clear: both; -} -.container-fluid { - padding-right: 20px; - padding-left: 20px; - *zoom: 1; -} -.container-fluid:before, -.container-fluid:after { - display: table; - content: ""; - line-height: 0; -} -.container-fluid:after { - clear: both; -} -p { - margin: 0 0 10px; -} -.lead { - margin-bottom: 20px; - font-size: 21px; - font-weight: 200; - line-height: 30px; -} -small { - font-size: 85%; -} -strong { - font-weight: bold; -} -em { - font-style: italic; -} -cite { - font-style: normal; -} -.muted { - color: #999999; -} -a.muted:hover, -a.muted:focus { - color: #808080; -} -.text-warning { - color: #c09853; -} -a.text-warning:hover, -a.text-warning:focus { - color: #a47e3c; -} -.text-error { - color: #b94a48; -} -a.text-error:hover, -a.text-error:focus { - color: #953b39; -} -.text-info { - color: #3a87ad; -} -a.text-info:hover, -a.text-info:focus { - color: #2d6987; -} -.text-success { - color: #468847; -} -a.text-success:hover, -a.text-success:focus { - color: #356635; -} -.text-left { - text-align: left; -} -.text-right { - text-align: right; -} -.text-center { - text-align: center; -} -h1, -h2, -h3, -h4, -h5, -h6 { - margin: 10px 0; - font-family: inherit; - font-weight: bold; - line-height: 20px; - color: inherit; - text-rendering: optimizelegibility; -} -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small { - font-weight: normal; - line-height: 1; - color: #999999; -} -h1, -h2, -h3 { - line-height: 40px; -} -h1 { - font-size: 38.5px; -} -h2 { - font-size: 31.5px; -} -h3 { - font-size: 24.5px; -} -h4 { - font-size: 17.5px; -} -h5 { - font-size: 14px; -} -h6 { - font-size: 11.9px; -} -h1 small { - font-size: 24.5px; -} -h2 small { - font-size: 17.5px; -} -h3 small { - font-size: 14px; -} -h4 small { - font-size: 14px; -} -.page-header { - padding-bottom: 9px; - margin: 20px 0 30px; - border-bottom: 1px solid #eeeeee; -} -ul, -ol { - padding: 0; - margin: 0 0 10px 25px; -} -ul ul, -ul ol, -ol ol, -ol ul { - margin-bottom: 0; -} -li { - line-height: 20px; -} -ul.unstyled, -ol.unstyled { - margin-left: 0; - list-style: none; -} -ul.inline, -ol.inline { - margin-left: 0; - list-style: none; -} -ul.inline > li, -ol.inline > li { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - *zoom: 1; - padding-left: 5px; - padding-right: 5px; -} -dl { - margin-bottom: 20px; -} -dt, -dd { - line-height: 20px; -} -dt { - font-weight: bold; -} -dd { - margin-left: 10px; -} -.dl-horizontal { - *zoom: 1; -} -.dl-horizontal:before, -.dl-horizontal:after { - display: table; - content: ""; - line-height: 0; -} -.dl-horizontal:after { - clear: both; -} -.dl-horizontal dt { - float: left; - width: 160px; - clear: left; - text-align: right; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.dl-horizontal dd { - margin-left: 180px; -} -hr { - margin: 20px 0; - border: 0; - border-top: 1px solid #eeeeee; - border-bottom: 1px solid #ffffff; -} -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #999999; -} -abbr.initialism { - font-size: 90%; - text-transform: uppercase; -} -blockquote { - padding: 0 0 0 15px; - margin: 0 0 20px; - border-left: 5px solid #eeeeee; -} -blockquote p { - margin-bottom: 0; - font-size: 17.5px; - font-weight: 300; - line-height: 1.25; -} -blockquote small { - display: block; - line-height: 20px; - color: #999999; -} -blockquote small:before { - content: '\2014 \00A0'; -} -blockquote.pull-right { - float: right; - padding-right: 15px; - padding-left: 0; - border-right: 5px solid #eeeeee; - border-left: 0; -} -blockquote.pull-right p, -blockquote.pull-right small { - text-align: right; -} -blockquote.pull-right small:before { - content: ''; -} -blockquote.pull-right small:after { - content: '\00A0 \2014'; -} -q:before, -q:after, -blockquote:before, -blockquote:after { - content: ""; -} -address { - display: block; - margin-bottom: 20px; - font-style: normal; - line-height: 20px; -} -code, -pre { - padding: 0 3px 2px; - font-family: Monaco, Menlo, Consolas, "Courier New", monospace; - font-size: 12px; - color: #333333; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -code { - padding: 2px 4px; - color: #d14; - background-color: #f7f7f9; - border: 1px solid #e1e1e8; - white-space: nowrap; -} -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 20px; - word-break: break-all; - word-wrap: break-word; - white-space: pre; - white-space: pre-wrap; - background-color: #f5f5f5; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.15); - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -pre.prettyprint { - margin-bottom: 20px; -} -pre code { - padding: 0; - color: inherit; - white-space: pre; - white-space: pre-wrap; - background-color: transparent; - border: 0; -} -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} -form { - margin: 0 0 20px; -} -fieldset { - padding: 0; - margin: 0; - border: 0; -} -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: 40px; - color: #333333; - border: 0; - border-bottom: 1px solid #e5e5e5; -} -legend small { - font-size: 15px; - color: #999999; -} -label, -input, -button, -select, -textarea { - font-size: 14px; - font-weight: normal; - line-height: 20px; -} -input, -button, -select, -textarea { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; -} -label { - display: block; - margin-bottom: 5px; -} -select, -textarea, -input[type="text"], -input[type="password"], -input[type="datetime"], -input[type="datetime-local"], -input[type="date"], -input[type="month"], -input[type="time"], -input[type="week"], -input[type="number"], -input[type="email"], -input[type="url"], -input[type="search"], -input[type="tel"], -input[type="color"], -.uneditable-input { - display: inline-block; - height: 20px; - padding: 4px 6px; - margin-bottom: 10px; - font-size: 14px; - line-height: 20px; - color: #555555; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - vertical-align: middle; -} -input, -textarea, -.uneditable-input { - width: 206px; -} -textarea { - height: auto; -} -textarea, -input[type="text"], -input[type="password"], -input[type="datetime"], -input[type="datetime-local"], -input[type="date"], -input[type="month"], -input[type="time"], -input[type="week"], -input[type="number"], -input[type="email"], -input[type="url"], -input[type="search"], -input[type="tel"], -input[type="color"], -.uneditable-input { - background-color: #ffffff; - border: 1px solid #cccccc; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -webkit-transition: border linear .2s, box-shadow linear .2s; - -moz-transition: border linear .2s, box-shadow linear .2s; - -o-transition: border linear .2s, box-shadow linear .2s; - transition: border linear .2s, box-shadow linear .2s; -} -textarea:focus, -input[type="text"]:focus, -input[type="password"]:focus, -input[type="datetime"]:focus, -input[type="datetime-local"]:focus, -input[type="date"]:focus, -input[type="month"]:focus, -input[type="time"]:focus, -input[type="week"]:focus, -input[type="number"]:focus, -input[type="email"]:focus, -input[type="url"]:focus, -input[type="search"]:focus, -input[type="tel"]:focus, -input[type="color"]:focus, -.uneditable-input:focus { - border-color: rgba(82, 168, 236, 0.8); - outline: 0; - outline: thin dotted \9; - /* IE6-9 */ - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); - -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); -} -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - *margin-top: 0; - /* IE7 */ - margin-top: 1px \9; - /* IE8-9 */ - line-height: normal; -} -input[type="file"], -input[type="image"], -input[type="submit"], -input[type="reset"], -input[type="button"], -input[type="radio"], -input[type="checkbox"] { - width: auto; -} -select, -input[type="file"] { - height: 30px; - /* In IE7, the height of the select element cannot be changed by height, only font-size */ - *margin-top: 4px; - /* For IE7, add top margin to align select with labels */ - line-height: 30px; -} -select { - width: 220px; - border: 1px solid #cccccc; - background-color: #ffffff; -} -select[multiple], -select[size] { - height: auto; -} -select:focus, -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.uneditable-input, -.uneditable-textarea { - color: #999999; - background-color: #fcfcfc; - border-color: #cccccc; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); - cursor: not-allowed; -} -.uneditable-input { - overflow: hidden; - white-space: nowrap; -} -.uneditable-textarea { - width: auto; - height: auto; -} -input:-moz-placeholder, -textarea:-moz-placeholder { - color: #999999; -} -input:-ms-input-placeholder, -textarea:-ms-input-placeholder { - color: #999999; -} -input::-webkit-input-placeholder, -textarea::-webkit-input-placeholder { - color: #999999; -} -.radio, -.checkbox { - min-height: 20px; - padding-left: 20px; -} -.radio input[type="radio"], -.checkbox input[type="checkbox"] { - float: left; - margin-left: -20px; -} -.controls > .radio:first-child, -.controls > .checkbox:first-child { - padding-top: 5px; -} -.radio.inline, -.checkbox.inline { - display: inline-block; - padding-top: 5px; - margin-bottom: 0; - vertical-align: middle; -} -.radio.inline + .radio.inline, -.checkbox.inline + .checkbox.inline { - margin-left: 10px; -} -.input-mini { - width: 60px; -} -.input-small { - width: 90px; -} -.input-medium { - width: 150px; -} -.input-large { - width: 210px; -} -.input-xlarge { - width: 270px; -} -.input-xxlarge { - width: 530px; -} -input[class*="span"], -select[class*="span"], -textarea[class*="span"], -.uneditable-input[class*="span"], -.row-fluid input[class*="span"], -.row-fluid select[class*="span"], -.row-fluid textarea[class*="span"], -.row-fluid .uneditable-input[class*="span"] { - float: none; - margin-left: 0; -} -.input-append input[class*="span"], -.input-append .uneditable-input[class*="span"], -.input-prepend input[class*="span"], -.input-prepend .uneditable-input[class*="span"], -.row-fluid input[class*="span"], -.row-fluid select[class*="span"], -.row-fluid textarea[class*="span"], -.row-fluid .uneditable-input[class*="span"], -.row-fluid .input-prepend [class*="span"], -.row-fluid .input-append [class*="span"] { - display: inline-block; -} -input, -textarea, -.uneditable-input { - margin-left: 0; -} -.controls-row [class*="span"] + [class*="span"] { - margin-left: 20px; -} -input.span12, -textarea.span12, -.uneditable-input.span12 { - width: 926px; -} -input.span11, -textarea.span11, -.uneditable-input.span11 { - width: 846px; -} -input.span10, -textarea.span10, -.uneditable-input.span10 { - width: 766px; -} -input.span9, -textarea.span9, -.uneditable-input.span9 { - width: 686px; -} -input.span8, -textarea.span8, -.uneditable-input.span8 { - width: 606px; -} -input.span7, -textarea.span7, -.uneditable-input.span7 { - width: 526px; -} -input.span6, -textarea.span6, -.uneditable-input.span6 { - width: 446px; -} -input.span5, -textarea.span5, -.uneditable-input.span5 { - width: 366px; -} -input.span4, -textarea.span4, -.uneditable-input.span4 { - width: 286px; -} -input.span3, -textarea.span3, -.uneditable-input.span3 { - width: 206px; -} -input.span2, -textarea.span2, -.uneditable-input.span2 { - width: 126px; -} -input.span1, -textarea.span1, -.uneditable-input.span1 { - width: 46px; -} -.controls-row { - *zoom: 1; -} -.controls-row:before, -.controls-row:after { - display: table; - content: ""; - line-height: 0; -} -.controls-row:after { - clear: both; -} -.controls-row [class*="span"], -.row-fluid .controls-row [class*="span"] { - float: left; -} -.controls-row .checkbox[class*="span"], -.controls-row .radio[class*="span"] { - padding-top: 5px; -} -input[disabled], -select[disabled], -textarea[disabled], -input[readonly], -select[readonly], -textarea[readonly] { - cursor: not-allowed; - background-color: #eeeeee; -} -input[type="radio"][disabled], -input[type="checkbox"][disabled], -input[type="radio"][readonly], -input[type="checkbox"][readonly] { - background-color: transparent; -} -.control-group.warning .control-label, -.control-group.warning .help-block, -.control-group.warning .help-inline { - color: #c09853; -} -.control-group.warning .checkbox, -.control-group.warning .radio, -.control-group.warning input, -.control-group.warning select, -.control-group.warning textarea { - color: #c09853; -} -.control-group.warning input, -.control-group.warning select, -.control-group.warning textarea { - border-color: #c09853; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} -.control-group.warning input:focus, -.control-group.warning select:focus, -.control-group.warning textarea:focus { - border-color: #a47e3c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; -} -.control-group.warning .input-prepend .add-on, -.control-group.warning .input-append .add-on { - color: #c09853; - background-color: #fcf8e3; - border-color: #c09853; -} -.control-group.error .control-label, -.control-group.error .help-block, -.control-group.error .help-inline { - color: #b94a48; -} -.control-group.error .checkbox, -.control-group.error .radio, -.control-group.error input, -.control-group.error select, -.control-group.error textarea { - color: #b94a48; -} -.control-group.error input, -.control-group.error select, -.control-group.error textarea { - border-color: #b94a48; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} -.control-group.error input:focus, -.control-group.error select:focus, -.control-group.error textarea:focus { - border-color: #953b39; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; -} -.control-group.error .input-prepend .add-on, -.control-group.error .input-append .add-on { - color: #b94a48; - background-color: #f2dede; - border-color: #b94a48; -} -.control-group.success .control-label, -.control-group.success .help-block, -.control-group.success .help-inline { - color: #468847; -} -.control-group.success .checkbox, -.control-group.success .radio, -.control-group.success input, -.control-group.success select, -.control-group.success textarea { - color: #468847; -} -.control-group.success input, -.control-group.success select, -.control-group.success textarea { - border-color: #468847; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} -.control-group.success input:focus, -.control-group.success select:focus, -.control-group.success textarea:focus { - border-color: #356635; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; -} -.control-group.success .input-prepend .add-on, -.control-group.success .input-append .add-on { - color: #468847; - background-color: #dff0d8; - border-color: #468847; -} -.control-group.info .control-label, -.control-group.info .help-block, -.control-group.info .help-inline { - color: #3a87ad; -} -.control-group.info .checkbox, -.control-group.info .radio, -.control-group.info input, -.control-group.info select, -.control-group.info textarea { - color: #3a87ad; -} -.control-group.info input, -.control-group.info select, -.control-group.info textarea { - border-color: #3a87ad; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} -.control-group.info input:focus, -.control-group.info select:focus, -.control-group.info textarea:focus { - border-color: #2d6987; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; -} -.control-group.info .input-prepend .add-on, -.control-group.info .input-append .add-on { - color: #3a87ad; - background-color: #d9edf7; - border-color: #3a87ad; -} -input:focus:invalid, -textarea:focus:invalid, -select:focus:invalid { - color: #b94a48; - border-color: #ee5f5b; -} -input:focus:invalid:focus, -textarea:focus:invalid:focus, -select:focus:invalid:focus { - border-color: #e9322d; - -webkit-box-shadow: 0 0 6px #f8b9b7; - -moz-box-shadow: 0 0 6px #f8b9b7; - box-shadow: 0 0 6px #f8b9b7; -} -.form-actions { - padding: 19px 20px 20px; - margin-top: 20px; - margin-bottom: 20px; - background-color: #f5f5f5; - border-top: 1px solid #e5e5e5; - *zoom: 1; -} -.form-actions:before, -.form-actions:after { - display: table; - content: ""; - line-height: 0; -} -.form-actions:after { - clear: both; -} -.help-block, -.help-inline { - color: #595959; -} -.help-block { - display: block; - margin-bottom: 10px; -} -.help-inline { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - *zoom: 1; - vertical-align: middle; - padding-left: 5px; -} -.input-append, -.input-prepend { - display: inline-block; - margin-bottom: 10px; - vertical-align: middle; - font-size: 0; - white-space: nowrap; -} -.input-append input, -.input-prepend input, -.input-append select, -.input-prepend select, -.input-append .uneditable-input, -.input-prepend .uneditable-input, -.input-append .dropdown-menu, -.input-prepend .dropdown-menu, -.input-append .popover, -.input-prepend .popover { - font-size: 14px; -} -.input-append input, -.input-prepend input, -.input-append select, -.input-prepend select, -.input-append .uneditable-input, -.input-prepend .uneditable-input { - position: relative; - margin-bottom: 0; - *margin-left: 0; - vertical-align: top; - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} -.input-append input:focus, -.input-prepend input:focus, -.input-append select:focus, -.input-prepend select:focus, -.input-append .uneditable-input:focus, -.input-prepend .uneditable-input:focus { - z-index: 2; -} -.input-append .add-on, -.input-prepend .add-on { - display: inline-block; - width: auto; - height: 20px; - min-width: 16px; - padding: 4px 5px; - font-size: 14px; - font-weight: normal; - line-height: 20px; - text-align: center; - text-shadow: 0 1px 0 #ffffff; - background-color: #eeeeee; - border: 1px solid #ccc; -} -.input-append .add-on, -.input-prepend .add-on, -.input-append .btn, -.input-prepend .btn, -.input-append .btn-group > .dropdown-toggle, -.input-prepend .btn-group > .dropdown-toggle { - vertical-align: top; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.input-append .active, -.input-prepend .active { - background-color: #a9dba9; - border-color: #46a546; -} -.input-prepend .add-on, -.input-prepend .btn { - margin-right: -1px; -} -.input-prepend .add-on:first-child, -.input-prepend .btn:first-child { - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} -.input-append input, -.input-append select, -.input-append .uneditable-input { - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} -.input-append input + .btn-group .btn:last-child, -.input-append select + .btn-group .btn:last-child, -.input-append .uneditable-input + .btn-group .btn:last-child { - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} -.input-append .add-on, -.input-append .btn, -.input-append .btn-group { - margin-left: -1px; -} -.input-append .add-on:last-child, -.input-append .btn:last-child, -.input-append .btn-group:last-child > .dropdown-toggle { - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} -.input-prepend.input-append input, -.input-prepend.input-append select, -.input-prepend.input-append .uneditable-input { - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.input-prepend.input-append input + .btn-group .btn, -.input-prepend.input-append select + .btn-group .btn, -.input-prepend.input-append .uneditable-input + .btn-group .btn { - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} -.input-prepend.input-append .add-on:first-child, -.input-prepend.input-append .btn:first-child { - margin-right: -1px; - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} -.input-prepend.input-append .add-on:last-child, -.input-prepend.input-append .btn:last-child { - margin-left: -1px; - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} -.input-prepend.input-append .btn-group:first-child { - margin-left: 0; -} -input.search-query { - padding-right: 14px; - padding-right: 4px \9; - padding-left: 14px; - padding-left: 4px \9; - /* IE7-8 doesn't have border-radius, so don't indent the padding */ - margin-bottom: 0; - -webkit-border-radius: 15px; - -moz-border-radius: 15px; - border-radius: 15px; -} -/* Allow for input prepend/append in search forms */ -.form-search .input-append .search-query, -.form-search .input-prepend .search-query { - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.form-search .input-append .search-query { - -webkit-border-radius: 14px 0 0 14px; - -moz-border-radius: 14px 0 0 14px; - border-radius: 14px 0 0 14px; -} -.form-search .input-append .btn { - -webkit-border-radius: 0 14px 14px 0; - -moz-border-radius: 0 14px 14px 0; - border-radius: 0 14px 14px 0; -} -.form-search .input-prepend .search-query { - -webkit-border-radius: 0 14px 14px 0; - -moz-border-radius: 0 14px 14px 0; - border-radius: 0 14px 14px 0; -} -.form-search .input-prepend .btn { - -webkit-border-radius: 14px 0 0 14px; - -moz-border-radius: 14px 0 0 14px; - border-radius: 14px 0 0 14px; -} -.form-search input, -.form-inline input, -.form-horizontal input, -.form-search textarea, -.form-inline textarea, -.form-horizontal textarea, -.form-search select, -.form-inline select, -.form-horizontal select, -.form-search .help-inline, -.form-inline .help-inline, -.form-horizontal .help-inline, -.form-search .uneditable-input, -.form-inline .uneditable-input, -.form-horizontal .uneditable-input, -.form-search .input-prepend, -.form-inline .input-prepend, -.form-horizontal .input-prepend, -.form-search .input-append, -.form-inline .input-append, -.form-horizontal .input-append { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - *zoom: 1; - margin-bottom: 0; - vertical-align: middle; -} -.form-search .hide, -.form-inline .hide, -.form-horizontal .hide { - display: none; -} -.form-search label, -.form-inline label, -.form-search .btn-group, -.form-inline .btn-group { - display: inline-block; -} -.form-search .input-append, -.form-inline .input-append, -.form-search .input-prepend, -.form-inline .input-prepend { - margin-bottom: 0; -} -.form-search .radio, -.form-search .checkbox, -.form-inline .radio, -.form-inline .checkbox { - padding-left: 0; - margin-bottom: 0; - vertical-align: middle; -} -.form-search .radio input[type="radio"], -.form-search .checkbox input[type="checkbox"], -.form-inline .radio input[type="radio"], -.form-inline .checkbox input[type="checkbox"] { - float: left; - margin-right: 3px; - margin-left: 0; -} -.control-group { - margin-bottom: 10px; -} -legend + .control-group { - margin-top: 20px; - -webkit-margin-top-collapse: separate; -} -.form-horizontal .control-group { - margin-bottom: 20px; - *zoom: 1; -} -.form-horizontal .control-group:before, -.form-horizontal .control-group:after { - display: table; - content: ""; - line-height: 0; -} -.form-horizontal .control-group:after { - clear: both; -} -.form-horizontal .control-label { - float: left; - width: 160px; - padding-top: 5px; - text-align: right; -} -.form-horizontal .controls { - *display: inline-block; - *padding-left: 20px; - margin-left: 180px; - *margin-left: 0; -} -.form-horizontal .controls:first-child { - *padding-left: 180px; -} -.form-horizontal .help-block { - margin-bottom: 0; -} -.form-horizontal input + .help-block, -.form-horizontal select + .help-block, -.form-horizontal textarea + .help-block, -.form-horizontal .uneditable-input + .help-block, -.form-horizontal .input-prepend + .help-block, -.form-horizontal .input-append + .help-block { - margin-top: 10px; -} -.form-horizontal .form-actions { - padding-left: 180px; -} -table { - max-width: 100%; - background-color: transparent; - border-collapse: collapse; - border-spacing: 0; -} -.table { - width: 100%; - margin-bottom: 20px; -} -.table th, -.table td { - padding: 8px; - line-height: 20px; - text-align: left; - vertical-align: top; - border-top: 1px solid #dddddd; -} -.table th { - font-weight: bold; -} -.table thead th { - vertical-align: bottom; -} -.table caption + thead tr:first-child th, -.table caption + thead tr:first-child td, -.table colgroup + thead tr:first-child th, -.table colgroup + thead tr:first-child td, -.table thead:first-child tr:first-child th, -.table thead:first-child tr:first-child td { - border-top: 0; -} -.table tbody + tbody { - border-top: 2px solid #dddddd; -} -.table .table { - background-color: #ffffff; -} -.table-condensed th, -.table-condensed td { - padding: 4px 5px; -} -.table-bordered { - border: 1px solid #dddddd; - border-collapse: separate; - *border-collapse: collapse; - border-left: 0; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.table-bordered th, -.table-bordered td { - border-left: 1px solid #dddddd; -} -.table-bordered caption + thead tr:first-child th, -.table-bordered caption + tbody tr:first-child th, -.table-bordered caption + tbody tr:first-child td, -.table-bordered colgroup + thead tr:first-child th, -.table-bordered colgroup + tbody tr:first-child th, -.table-bordered colgroup + tbody tr:first-child td, -.table-bordered thead:first-child tr:first-child th, -.table-bordered tbody:first-child tr:first-child th, -.table-bordered tbody:first-child tr:first-child td { - border-top: 0; -} -.table-bordered thead:first-child tr:first-child > th:first-child, -.table-bordered tbody:first-child tr:first-child > td:first-child, -.table-bordered tbody:first-child tr:first-child > th:first-child { - -webkit-border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; - border-top-left-radius: 4px; -} -.table-bordered thead:first-child tr:first-child > th:last-child, -.table-bordered tbody:first-child tr:first-child > td:last-child, -.table-bordered tbody:first-child tr:first-child > th:last-child { - -webkit-border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; - border-top-right-radius: 4px; -} -.table-bordered thead:last-child tr:last-child > th:first-child, -.table-bordered tbody:last-child tr:last-child > td:first-child, -.table-bordered tbody:last-child tr:last-child > th:first-child, -.table-bordered tfoot:last-child tr:last-child > td:first-child, -.table-bordered tfoot:last-child tr:last-child > th:first-child { - -webkit-border-bottom-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - border-bottom-left-radius: 4px; -} -.table-bordered thead:last-child tr:last-child > th:last-child, -.table-bordered tbody:last-child tr:last-child > td:last-child, -.table-bordered tbody:last-child tr:last-child > th:last-child, -.table-bordered tfoot:last-child tr:last-child > td:last-child, -.table-bordered tfoot:last-child tr:last-child > th:last-child { - -webkit-border-bottom-right-radius: 4px; - -moz-border-radius-bottomright: 4px; - border-bottom-right-radius: 4px; -} -.table-bordered tfoot + tbody:last-child tr:last-child td:first-child { - -webkit-border-bottom-left-radius: 0; - -moz-border-radius-bottomleft: 0; - border-bottom-left-radius: 0; -} -.table-bordered tfoot + tbody:last-child tr:last-child td:last-child { - -webkit-border-bottom-right-radius: 0; - -moz-border-radius-bottomright: 0; - border-bottom-right-radius: 0; -} -.table-bordered caption + thead tr:first-child th:first-child, -.table-bordered caption + tbody tr:first-child td:first-child, -.table-bordered colgroup + thead tr:first-child th:first-child, -.table-bordered colgroup + tbody tr:first-child td:first-child { - -webkit-border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; - border-top-left-radius: 4px; -} -.table-bordered caption + thead tr:first-child th:last-child, -.table-bordered caption + tbody tr:first-child td:last-child, -.table-bordered colgroup + thead tr:first-child th:last-child, -.table-bordered colgroup + tbody tr:first-child td:last-child { - -webkit-border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; - border-top-right-radius: 4px; -} -.table-striped tbody > tr:nth-child(odd) > td, -.table-striped tbody > tr:nth-child(odd) > th { - background-color: #f9f9f9; -} -.table-hover tbody tr:hover > td, -.table-hover tbody tr:hover > th { - background-color: #f5f5f5; -} -table td[class*="span"], -table th[class*="span"], -.row-fluid table td[class*="span"], -.row-fluid table th[class*="span"] { - display: table-cell; - float: none; - margin-left: 0; -} -.table td.span1, -.table th.span1 { - float: none; - width: 44px; - margin-left: 0; -} -.table td.span2, -.table th.span2 { - float: none; - width: 124px; - margin-left: 0; -} -.table td.span3, -.table th.span3 { - float: none; - width: 204px; - margin-left: 0; -} -.table td.span4, -.table th.span4 { - float: none; - width: 284px; - margin-left: 0; -} -.table td.span5, -.table th.span5 { - float: none; - width: 364px; - margin-left: 0; -} -.table td.span6, -.table th.span6 { - float: none; - width: 444px; - margin-left: 0; -} -.table td.span7, -.table th.span7 { - float: none; - width: 524px; - margin-left: 0; -} -.table td.span8, -.table th.span8 { - float: none; - width: 604px; - margin-left: 0; -} -.table td.span9, -.table th.span9 { - float: none; - width: 684px; - margin-left: 0; -} -.table td.span10, -.table th.span10 { - float: none; - width: 764px; - margin-left: 0; -} -.table td.span11, -.table th.span11 { - float: none; - width: 844px; - margin-left: 0; -} -.table td.span12, -.table th.span12 { - float: none; - width: 924px; - margin-left: 0; -} -.table tbody tr.success > td { - background-color: #dff0d8; -} -.table tbody tr.error > td { - background-color: #f2dede; -} -.table tbody tr.warning > td { - background-color: #fcf8e3; -} -.table tbody tr.info > td { - background-color: #d9edf7; -} -.table-hover tbody tr.success:hover > td { - background-color: #d0e9c6; -} -.table-hover tbody tr.error:hover > td { - background-color: #ebcccc; -} -.table-hover tbody tr.warning:hover > td { - background-color: #faf2cc; -} -.table-hover tbody tr.info:hover > td { - background-color: #c4e3f3; -} -[class^="icon-"], -[class*=" icon-"] { - display: inline-block; - width: 14px; - height: 14px; - *margin-right: .3em; - line-height: 14px; - vertical-align: text-top; - background-image: url("../img/glyphicons-halflings.png"); - background-position: 14px 14px; - background-repeat: no-repeat; - margin-top: 1px; -} -/* White icons with optional class, or on hover/focus/active states of certain elements */ -.icon-white, -.nav-pills > .active > a > [class^="icon-"], -.nav-pills > .active > a > [class*=" icon-"], -.nav-list > .active > a > [class^="icon-"], -.nav-list > .active > a > [class*=" icon-"], -.navbar-inverse .nav > .active > a > [class^="icon-"], -.navbar-inverse .nav > .active > a > [class*=" icon-"], -.dropdown-menu > li > a:hover > [class^="icon-"], -.dropdown-menu > li > a:focus > [class^="icon-"], -.dropdown-menu > li > a:hover > [class*=" icon-"], -.dropdown-menu > li > a:focus > [class*=" icon-"], -.dropdown-menu > .active > a > [class^="icon-"], -.dropdown-menu > .active > a > [class*=" icon-"], -.dropdown-submenu:hover > a > [class^="icon-"], -.dropdown-submenu:focus > a > [class^="icon-"], -.dropdown-submenu:hover > a > [class*=" icon-"], -.dropdown-submenu:focus > a > [class*=" icon-"] { - background-image: url("../img/glyphicons-halflings-white.png"); -} -.icon-glass { - background-position: 0 0; -} -.icon-music { - background-position: -24px 0; -} -.icon-search { - background-position: -48px 0; -} -.icon-envelope { - background-position: -72px 0; -} -.icon-heart { - background-position: -96px 0; -} -.icon-star { - background-position: -120px 0; -} -.icon-star-empty { - background-position: -144px 0; -} -.icon-user { - background-position: -168px 0; -} -.icon-film { - background-position: -192px 0; -} -.icon-th-large { - background-position: -216px 0; -} -.icon-th { - background-position: -240px 0; -} -.icon-th-list { - background-position: -264px 0; -} -.icon-ok { - background-position: -288px 0; -} -.icon-remove { - background-position: -312px 0; -} -.icon-zoom-in { - background-position: -336px 0; -} -.icon-zoom-out { - background-position: -360px 0; -} -.icon-off { - background-position: -384px 0; -} -.icon-signal { - background-position: -408px 0; -} -.icon-cog { - background-position: -432px 0; -} -.icon-trash { - background-position: -456px 0; -} -.icon-home { - background-position: 0 -24px; -} -.icon-file { - background-position: -24px -24px; -} -.icon-time { - background-position: -48px -24px; -} -.icon-road { - background-position: -72px -24px; -} -.icon-download-alt { - background-position: -96px -24px; -} -.icon-download { - background-position: -120px -24px; -} -.icon-upload { - background-position: -144px -24px; -} -.icon-inbox { - background-position: -168px -24px; -} -.icon-play-circle { - background-position: -192px -24px; -} -.icon-repeat { - background-position: -216px -24px; -} -.icon-refresh { - background-position: -240px -24px; -} -.icon-list-alt { - background-position: -264px -24px; -} -.icon-lock { - background-position: -287px -24px; -} -.icon-flag { - background-position: -312px -24px; -} -.icon-headphones { - background-position: -336px -24px; -} -.icon-volume-off { - background-position: -360px -24px; -} -.icon-volume-down { - background-position: -384px -24px; -} -.icon-volume-up { - background-position: -408px -24px; -} -.icon-qrcode { - background-position: -432px -24px; -} -.icon-barcode { - background-position: -456px -24px; -} -.icon-tag { - background-position: 0 -48px; -} -.icon-tags { - background-position: -25px -48px; -} -.icon-book { - background-position: -48px -48px; -} -.icon-bookmark { - background-position: -72px -48px; -} -.icon-print { - background-position: -96px -48px; -} -.icon-camera { - background-position: -120px -48px; -} -.icon-font { - background-position: -144px -48px; -} -.icon-bold { - background-position: -167px -48px; -} -.icon-italic { - background-position: -192px -48px; -} -.icon-text-height { - background-position: -216px -48px; -} -.icon-text-width { - background-position: -240px -48px; -} -.icon-align-left { - background-position: -264px -48px; -} -.icon-align-center { - background-position: -288px -48px; -} -.icon-align-right { - background-position: -312px -48px; -} -.icon-align-justify { - background-position: -336px -48px; -} -.icon-list { - background-position: -360px -48px; -} -.icon-indent-left { - background-position: -384px -48px; -} -.icon-indent-right { - background-position: -408px -48px; -} -.icon-facetime-video { - background-position: -432px -48px; -} -.icon-picture { - background-position: -456px -48px; -} -.icon-pencil { - background-position: 0 -72px; -} -.icon-map-marker { - background-position: -24px -72px; -} -.icon-adjust { - background-position: -48px -72px; -} -.icon-tint { - background-position: -72px -72px; -} -.icon-edit { - background-position: -96px -72px; -} -.icon-share { - background-position: -120px -72px; -} -.icon-check { - background-position: -144px -72px; -} -.icon-move { - background-position: -168px -72px; -} -.icon-step-backward { - background-position: -192px -72px; -} -.icon-fast-backward { - background-position: -216px -72px; -} -.icon-backward { - background-position: -240px -72px; -} -.icon-play { - background-position: -264px -72px; -} -.icon-pause { - background-position: -288px -72px; -} -.icon-stop { - background-position: -312px -72px; -} -.icon-forward { - background-position: -336px -72px; -} -.icon-fast-forward { - background-position: -360px -72px; -} -.icon-step-forward { - background-position: -384px -72px; -} -.icon-eject { - background-position: -408px -72px; -} -.icon-chevron-left { - background-position: -432px -72px; -} -.icon-chevron-right { - background-position: -456px -72px; -} -.icon-plus-sign { - background-position: 0 -96px; -} -.icon-minus-sign { - background-position: -24px -96px; -} -.icon-remove-sign { - background-position: -48px -96px; -} -.icon-ok-sign { - background-position: -72px -96px; -} -.icon-question-sign { - background-position: -96px -96px; -} -.icon-info-sign { - background-position: -120px -96px; -} -.icon-screenshot { - background-position: -144px -96px; -} -.icon-remove-circle { - background-position: -168px -96px; -} -.icon-ok-circle { - background-position: -192px -96px; -} -.icon-ban-circle { - background-position: -216px -96px; -} -.icon-arrow-left { - background-position: -240px -96px; -} -.icon-arrow-right { - background-position: -264px -96px; -} -.icon-arrow-up { - background-position: -289px -96px; -} -.icon-arrow-down { - background-position: -312px -96px; -} -.icon-share-alt { - background-position: -336px -96px; -} -.icon-resize-full { - background-position: -360px -96px; -} -.icon-resize-small { - background-position: -384px -96px; -} -.icon-plus { - background-position: -408px -96px; -} -.icon-minus { - background-position: -433px -96px; -} -.icon-asterisk { - background-position: -456px -96px; -} -.icon-exclamation-sign { - background-position: 0 -120px; -} -.icon-gift { - background-position: -24px -120px; -} -.icon-leaf { - background-position: -48px -120px; -} -.icon-fire { - background-position: -72px -120px; -} -.icon-eye-open { - background-position: -96px -120px; -} -.icon-eye-close { - background-position: -120px -120px; -} -.icon-warning-sign { - background-position: -144px -120px; -} -.icon-plane { - background-position: -168px -120px; -} -.icon-calendar { - background-position: -192px -120px; -} -.icon-random { - background-position: -216px -120px; - width: 16px; -} -.icon-comment { - background-position: -240px -120px; -} -.icon-magnet { - background-position: -264px -120px; -} -.icon-chevron-up { - background-position: -288px -120px; -} -.icon-chevron-down { - background-position: -313px -119px; -} -.icon-retweet { - background-position: -336px -120px; -} -.icon-shopping-cart { - background-position: -360px -120px; -} -.icon-folder-close { - background-position: -384px -120px; - width: 16px; -} -.icon-folder-open { - background-position: -408px -120px; - width: 16px; -} -.icon-resize-vertical { - background-position: -432px -119px; -} -.icon-resize-horizontal { - background-position: -456px -118px; -} -.icon-hdd { - background-position: 0 -144px; -} -.icon-bullhorn { - background-position: -24px -144px; -} -.icon-bell { - background-position: -48px -144px; -} -.icon-certificate { - background-position: -72px -144px; -} -.icon-thumbs-up { - background-position: -96px -144px; -} -.icon-thumbs-down { - background-position: -120px -144px; -} -.icon-hand-right { - background-position: -144px -144px; -} -.icon-hand-left { - background-position: -168px -144px; -} -.icon-hand-up { - background-position: -192px -144px; -} -.icon-hand-down { - background-position: -216px -144px; -} -.icon-circle-arrow-right { - background-position: -240px -144px; -} -.icon-circle-arrow-left { - background-position: -264px -144px; -} -.icon-circle-arrow-up { - background-position: -288px -144px; -} -.icon-circle-arrow-down { - background-position: -312px -144px; -} -.icon-globe { - background-position: -336px -144px; -} -.icon-wrench { - background-position: -360px -144px; -} -.icon-tasks { - background-position: -384px -144px; -} -.icon-filter { - background-position: -408px -144px; -} -.icon-briefcase { - background-position: -432px -144px; -} -.icon-fullscreen { - background-position: -456px -144px; -} -.dropup, -.dropdown { - position: relative; -} -.dropdown-toggle { - *margin-bottom: -3px; -} -.dropdown-toggle:active, -.open .dropdown-toggle { - outline: 0; -} -.caret { - display: inline-block; - width: 0; - height: 0; - vertical-align: top; - border-top: 4px solid #000000; - border-right: 4px solid transparent; - border-left: 4px solid transparent; - content: ""; -} -.dropdown .caret { - margin-top: 8px; - margin-left: 2px; -} -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - list-style: none; - background-color: #ffffff; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - *border-right-width: 2px; - *border-bottom-width: 2px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -webkit-background-clip: padding-box; - -moz-background-clip: padding; - background-clip: padding-box; -} -.dropdown-menu.pull-right { - right: 0; - left: auto; -} -.dropdown-menu .divider { - *width: 100%; - height: 1px; - margin: 9px 1px; - *margin: -5px 0 5px; - overflow: hidden; - background-color: #e5e5e5; - border-bottom: 1px solid #ffffff; -} -.dropdown-menu > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 20px; - color: #333333; - white-space: nowrap; -} -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus, -.dropdown-submenu:hover > a, -.dropdown-submenu:focus > a { - text-decoration: none; - color: #ffffff; - background-color: #0081c2; - background-image: -moz-linear-gradient(top, #0088cc, #0077b3); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); - background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); - background-image: -o-linear-gradient(top, #0088cc, #0077b3); - background-image: linear-gradient(to bottom, #0088cc, #0077b3); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); -} -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - color: #ffffff; - text-decoration: none; - outline: 0; - background-color: #0081c2; - background-image: -moz-linear-gradient(top, #0088cc, #0077b3); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); - background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); - background-image: -o-linear-gradient(top, #0088cc, #0077b3); - background-image: linear-gradient(to bottom, #0088cc, #0077b3); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); -} -.dropdown-menu > .disabled > a, -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - color: #999999; -} -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - text-decoration: none; - background-color: transparent; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - cursor: default; -} -.open { - *z-index: 1000; -} -.open > .dropdown-menu { - display: block; -} -.dropdown-backdrop { - position: fixed; - left: 0; - right: 0; - bottom: 0; - top: 0; - z-index: 990; -} -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} -.dropup .caret, -.navbar-fixed-bottom .dropdown .caret { - border-top: 0; - border-bottom: 4px solid #000000; - content: ""; -} -.dropup .dropdown-menu, -.navbar-fixed-bottom .dropdown .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 1px; -} -.dropdown-submenu { - position: relative; -} -.dropdown-submenu > .dropdown-menu { - top: 0; - left: 100%; - margin-top: -6px; - margin-left: -1px; - -webkit-border-radius: 0 6px 6px 6px; - -moz-border-radius: 0 6px 6px 6px; - border-radius: 0 6px 6px 6px; -} -.dropdown-submenu:hover > .dropdown-menu { - display: block; -} -.dropup .dropdown-submenu > .dropdown-menu { - top: auto; - bottom: 0; - margin-top: 0; - margin-bottom: -2px; - -webkit-border-radius: 5px 5px 5px 0; - -moz-border-radius: 5px 5px 5px 0; - border-radius: 5px 5px 5px 0; -} -.dropdown-submenu > a:after { - display: block; - content: " "; - float: right; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; - border-width: 5px 0 5px 5px; - border-left-color: #cccccc; - margin-top: 5px; - margin-right: -10px; -} -.dropdown-submenu:hover > a:after { - border-left-color: #ffffff; -} -.dropdown-submenu.pull-left { - float: none; -} -.dropdown-submenu.pull-left > .dropdown-menu { - left: -100%; - margin-left: 10px; - -webkit-border-radius: 6px 0 6px 6px; - -moz-border-radius: 6px 0 6px 6px; - border-radius: 6px 0 6px 6px; -} -.dropdown .dropdown-menu .nav-header { - padding-left: 20px; - padding-right: 20px; -} -.typeahead { - z-index: 1051; - margin-top: 2px; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: #f5f5f5; - border: 1px solid #e3e3e3; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); -} -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, 0.15); -} -.well-large { - padding: 24px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} -.well-small { - padding: 9px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -.fade { - opacity: 0; - -webkit-transition: opacity 0.15s linear; - -moz-transition: opacity 0.15s linear; - -o-transition: opacity 0.15s linear; - transition: opacity 0.15s linear; -} -.fade.in { - opacity: 1; -} -.collapse { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition: height 0.35s ease; - -moz-transition: height 0.35s ease; - -o-transition: height 0.35s ease; - transition: height 0.35s ease; -} -.collapse.in { - height: auto; -} -.close { - float: right; - font-size: 20px; - font-weight: bold; - line-height: 20px; - color: #000000; - text-shadow: 0 1px 0 #ffffff; - opacity: 0.2; - filter: alpha(opacity=20); -} -.close:hover, -.close:focus { - color: #000000; - text-decoration: none; - cursor: pointer; - opacity: 0.4; - filter: alpha(opacity=40); -} -button.close { - padding: 0; - cursor: pointer; - background: transparent; - border: 0; - -webkit-appearance: none; -} -.btn { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - *zoom: 1; - padding: 4px 12px; - margin-bottom: 0; - font-size: 14px; - line-height: 20px; - text-align: center; - vertical-align: middle; - cursor: pointer; - color: #333333; - text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); - background-color: #f5f5f5; - background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); - background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); - background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); - background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); - border-color: #e6e6e6 #e6e6e6 #bfbfbf; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #e6e6e6; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - border: 1px solid #cccccc; - *border: 0; - border-bottom-color: #b3b3b3; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - *margin-left: .3em; - -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); -} -.btn:hover, -.btn:focus, -.btn:active, -.btn.active, -.btn.disabled, -.btn[disabled] { - color: #333333; - background-color: #e6e6e6; - *background-color: #d9d9d9; -} -.btn:active, -.btn.active { - background-color: #cccccc \9; -} -.btn:first-child { - *margin-left: 0; -} -.btn:hover, -.btn:focus { - color: #333333; - text-decoration: none; - background-position: 0 -15px; - -webkit-transition: background-position 0.1s linear; - -moz-transition: background-position 0.1s linear; - -o-transition: background-position 0.1s linear; - transition: background-position 0.1s linear; -} -.btn:focus { - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.btn.active, -.btn:active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); - -moz-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); - box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); -} -.btn.disabled, -.btn[disabled] { - cursor: default; - background-image: none; - opacity: 0.65; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; -} -.btn-large { - padding: 11px 19px; - font-size: 17.5px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} -.btn-large [class^="icon-"], -.btn-large [class*=" icon-"] { - margin-top: 4px; -} -.btn-small { - padding: 2px 10px; - font-size: 11.9px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -.btn-small [class^="icon-"], -.btn-small [class*=" icon-"] { - margin-top: 0; -} -.btn-mini [class^="icon-"], -.btn-mini [class*=" icon-"] { - margin-top: -1px; -} -.btn-mini { - padding: 0 6px; - font-size: 10.5px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -.btn-block { - display: block; - width: 100%; - padding-left: 0; - padding-right: 0; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -.btn-block + .btn-block { - margin-top: 5px; -} -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} -.btn-primary.active, -.btn-warning.active, -.btn-danger.active, -.btn-success.active, -.btn-info.active, -.btn-inverse.active { - color: rgba(255, 255, 255, 0.75); -} -.btn-primary { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #006dcc; - background-image: -moz-linear-gradient(top, #0088cc, #0044cc); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); - background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); - background-image: -o-linear-gradient(top, #0088cc, #0044cc); - background-image: linear-gradient(to bottom, #0088cc, #0044cc); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); - border-color: #0044cc #0044cc #002a80; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #0044cc; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.btn-primary:hover, -.btn-primary:focus, -.btn-primary:active, -.btn-primary.active, -.btn-primary.disabled, -.btn-primary[disabled] { - color: #ffffff; - background-color: #0044cc; - *background-color: #003bb3; -} -.btn-primary:active, -.btn-primary.active { - background-color: #003399 \9; -} -.btn-warning { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #faa732; - background-image: -moz-linear-gradient(top, #fbb450, #f89406); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); - background-image: -webkit-linear-gradient(top, #fbb450, #f89406); - background-image: -o-linear-gradient(top, #fbb450, #f89406); - background-image: linear-gradient(to bottom, #fbb450, #f89406); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); - border-color: #f89406 #f89406 #ad6704; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #f89406; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.btn-warning:hover, -.btn-warning:focus, -.btn-warning:active, -.btn-warning.active, -.btn-warning.disabled, -.btn-warning[disabled] { - color: #ffffff; - background-color: #f89406; - *background-color: #df8505; -} -.btn-warning:active, -.btn-warning.active { - background-color: #c67605 \9; -} -.btn-danger { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #da4f49; - background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); - background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); - background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); - background-image: linear-gradient(to bottom, #ee5f5b, #bd362f); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0); - border-color: #bd362f #bd362f #802420; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #bd362f; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.btn-danger:hover, -.btn-danger:focus, -.btn-danger:active, -.btn-danger.active, -.btn-danger.disabled, -.btn-danger[disabled] { - color: #ffffff; - background-color: #bd362f; - *background-color: #a9302a; -} -.btn-danger:active, -.btn-danger.active { - background-color: #942a25 \9; -} -.btn-success { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #5bb75b; - background-image: -moz-linear-gradient(top, #62c462, #51a351); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); - background-image: -webkit-linear-gradient(top, #62c462, #51a351); - background-image: -o-linear-gradient(top, #62c462, #51a351); - background-image: linear-gradient(to bottom, #62c462, #51a351); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0); - border-color: #51a351 #51a351 #387038; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #51a351; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.btn-success:hover, -.btn-success:focus, -.btn-success:active, -.btn-success.active, -.btn-success.disabled, -.btn-success[disabled] { - color: #ffffff; - background-color: #51a351; - *background-color: #499249; -} -.btn-success:active, -.btn-success.active { - background-color: #408140 \9; -} -.btn-info { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #49afcd; - background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); - background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); - background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); - background-image: linear-gradient(to bottom, #5bc0de, #2f96b4); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0); - border-color: #2f96b4 #2f96b4 #1f6377; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #2f96b4; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.btn-info:hover, -.btn-info:focus, -.btn-info:active, -.btn-info.active, -.btn-info.disabled, -.btn-info[disabled] { - color: #ffffff; - background-color: #2f96b4; - *background-color: #2a85a0; -} -.btn-info:active, -.btn-info.active { - background-color: #24748c \9; -} -.btn-inverse { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #363636; - background-image: -moz-linear-gradient(top, #444444, #222222); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); - background-image: -webkit-linear-gradient(top, #444444, #222222); - background-image: -o-linear-gradient(top, #444444, #222222); - background-image: linear-gradient(to bottom, #444444, #222222); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); - border-color: #222222 #222222 #000000; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #222222; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.btn-inverse:hover, -.btn-inverse:focus, -.btn-inverse:active, -.btn-inverse.active, -.btn-inverse.disabled, -.btn-inverse[disabled] { - color: #ffffff; - background-color: #222222; - *background-color: #151515; -} -.btn-inverse:active, -.btn-inverse.active { - background-color: #080808 \9; -} -button.btn, -input[type="submit"].btn { - *padding-top: 3px; - *padding-bottom: 3px; -} -button.btn::-moz-focus-inner, -input[type="submit"].btn::-moz-focus-inner { - padding: 0; - border: 0; -} -button.btn.btn-large, -input[type="submit"].btn.btn-large { - *padding-top: 7px; - *padding-bottom: 7px; -} -button.btn.btn-small, -input[type="submit"].btn.btn-small { - *padding-top: 3px; - *padding-bottom: 3px; -} -button.btn.btn-mini, -input[type="submit"].btn.btn-mini { - *padding-top: 1px; - *padding-bottom: 1px; -} -.btn-link, -.btn-link:active, -.btn-link[disabled] { - background-color: transparent; - background-image: none; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; -} -.btn-link { - border-color: transparent; - cursor: pointer; - color: #0088cc; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.btn-link:hover, -.btn-link:focus { - color: #005580; - text-decoration: underline; - background-color: transparent; -} -.btn-link[disabled]:hover, -.btn-link[disabled]:focus { - color: #333333; - text-decoration: none; -} -.btn-group { - position: relative; - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - *zoom: 1; - font-size: 0; - vertical-align: middle; - white-space: nowrap; - *margin-left: .3em; -} -.btn-group:first-child { - *margin-left: 0; -} -.btn-group + .btn-group { - margin-left: 5px; -} -.btn-toolbar { - font-size: 0; - margin-top: 10px; - margin-bottom: 10px; -} -.btn-toolbar > .btn + .btn, -.btn-toolbar > .btn-group + .btn, -.btn-toolbar > .btn + .btn-group { - margin-left: 5px; -} -.btn-group > .btn { - position: relative; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.btn-group > .btn + .btn { - margin-left: -1px; -} -.btn-group > .btn, -.btn-group > .dropdown-menu, -.btn-group > .popover { - font-size: 14px; -} -.btn-group > .btn-mini { - font-size: 10.5px; -} -.btn-group > .btn-small { - font-size: 11.9px; -} -.btn-group > .btn-large { - font-size: 17.5px; -} -.btn-group > .btn:first-child { - margin-left: 0; - -webkit-border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; - border-top-left-radius: 4px; - -webkit-border-bottom-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - border-bottom-left-radius: 4px; -} -.btn-group > .btn:last-child, -.btn-group > .dropdown-toggle { - -webkit-border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; - border-top-right-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - -moz-border-radius-bottomright: 4px; - border-bottom-right-radius: 4px; -} -.btn-group > .btn.large:first-child { - margin-left: 0; - -webkit-border-top-left-radius: 6px; - -moz-border-radius-topleft: 6px; - border-top-left-radius: 6px; - -webkit-border-bottom-left-radius: 6px; - -moz-border-radius-bottomleft: 6px; - border-bottom-left-radius: 6px; -} -.btn-group > .btn.large:last-child, -.btn-group > .large.dropdown-toggle { - -webkit-border-top-right-radius: 6px; - -moz-border-radius-topright: 6px; - border-top-right-radius: 6px; - -webkit-border-bottom-right-radius: 6px; - -moz-border-radius-bottomright: 6px; - border-bottom-right-radius: 6px; -} -.btn-group > .btn:hover, -.btn-group > .btn:focus, -.btn-group > .btn:active, -.btn-group > .btn.active { - z-index: 2; -} -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} -.btn-group > .btn + .dropdown-toggle { - padding-left: 8px; - padding-right: 8px; - -webkit-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - -moz-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - *padding-top: 5px; - *padding-bottom: 5px; -} -.btn-group > .btn-mini + .dropdown-toggle { - padding-left: 5px; - padding-right: 5px; - *padding-top: 2px; - *padding-bottom: 2px; -} -.btn-group > .btn-small + .dropdown-toggle { - *padding-top: 5px; - *padding-bottom: 4px; -} -.btn-group > .btn-large + .dropdown-toggle { - padding-left: 12px; - padding-right: 12px; - *padding-top: 7px; - *padding-bottom: 7px; -} -.btn-group.open .dropdown-toggle { - background-image: none; - -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); - -moz-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); - box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); -} -.btn-group.open .btn.dropdown-toggle { - background-color: #e6e6e6; -} -.btn-group.open .btn-primary.dropdown-toggle { - background-color: #0044cc; -} -.btn-group.open .btn-warning.dropdown-toggle { - background-color: #f89406; -} -.btn-group.open .btn-danger.dropdown-toggle { - background-color: #bd362f; -} -.btn-group.open .btn-success.dropdown-toggle { - background-color: #51a351; -} -.btn-group.open .btn-info.dropdown-toggle { - background-color: #2f96b4; -} -.btn-group.open .btn-inverse.dropdown-toggle { - background-color: #222222; -} -.btn .caret { - margin-top: 8px; - margin-left: 0; -} -.btn-large .caret { - margin-top: 6px; -} -.btn-large .caret { - border-left-width: 5px; - border-right-width: 5px; - border-top-width: 5px; -} -.btn-mini .caret, -.btn-small .caret { - margin-top: 8px; -} -.dropup .btn-large .caret { - border-bottom-width: 5px; -} -.btn-primary .caret, -.btn-warning .caret, -.btn-danger .caret, -.btn-info .caret, -.btn-success .caret, -.btn-inverse .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; -} -.btn-group-vertical { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - *zoom: 1; -} -.btn-group-vertical > .btn { - display: block; - float: none; - max-width: 100%; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.btn-group-vertical > .btn + .btn { - margin-left: 0; - margin-top: -1px; -} -.btn-group-vertical > .btn:first-child { - -webkit-border-radius: 4px 4px 0 0; - -moz-border-radius: 4px 4px 0 0; - border-radius: 4px 4px 0 0; -} -.btn-group-vertical > .btn:last-child { - -webkit-border-radius: 0 0 4px 4px; - -moz-border-radius: 0 0 4px 4px; - border-radius: 0 0 4px 4px; -} -.btn-group-vertical > .btn-large:first-child { - -webkit-border-radius: 6px 6px 0 0; - -moz-border-radius: 6px 6px 0 0; - border-radius: 6px 6px 0 0; -} -.btn-group-vertical > .btn-large:last-child { - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; -} -.alert { - padding: 8px 35px 8px 14px; - margin-bottom: 20px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - background-color: #fcf8e3; - border: 1px solid #fbeed5; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.alert, -.alert h4 { - color: #c09853; -} -.alert h4 { - margin: 0; -} -.alert .close { - position: relative; - top: -2px; - right: -21px; - line-height: 20px; -} -.alert-success { - background-color: #dff0d8; - border-color: #d6e9c6; - color: #468847; -} -.alert-success h4 { - color: #468847; -} -.alert-danger, -.alert-error { - background-color: #f2dede; - border-color: #eed3d7; - color: #b94a48; -} -.alert-danger h4, -.alert-error h4 { - color: #b94a48; -} -.alert-info { - background-color: #d9edf7; - border-color: #bce8f1; - color: #3a87ad; -} -.alert-info h4 { - color: #3a87ad; -} -.alert-block { - padding-top: 14px; - padding-bottom: 14px; -} -.alert-block > p, -.alert-block > ul { - margin-bottom: 0; -} -.alert-block p + p { - margin-top: 5px; -} -.nav { - margin-left: 0; - margin-bottom: 20px; - list-style: none; -} -.nav > li > a { - display: block; -} -.nav > li > a:hover, -.nav > li > a:focus { - text-decoration: none; - background-color: #eeeeee; -} -.nav > li > a > img { - max-width: none; -} -.nav > .pull-right { - float: right; -} -.nav-header { - display: block; - padding: 3px 15px; - font-size: 11px; - font-weight: bold; - line-height: 20px; - color: #999999; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-transform: uppercase; -} -.nav li + .nav-header { - margin-top: 9px; -} -.nav-list { - padding-left: 15px; - padding-right: 15px; - margin-bottom: 0; -} -.nav-list > li > a, -.nav-list .nav-header { - margin-left: -15px; - margin-right: -15px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); -} -.nav-list > li > a { - padding: 3px 15px; -} -.nav-list > .active > a, -.nav-list > .active > a:hover, -.nav-list > .active > a:focus { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); - background-color: #0088cc; -} -.nav-list [class^="icon-"], -.nav-list [class*=" icon-"] { - margin-right: 2px; -} -.nav-list .divider { - *width: 100%; - height: 1px; - margin: 9px 1px; - *margin: -5px 0 5px; - overflow: hidden; - background-color: #e5e5e5; - border-bottom: 1px solid #ffffff; -} -.nav-tabs, -.nav-pills { - *zoom: 1; -} -.nav-tabs:before, -.nav-pills:before, -.nav-tabs:after, -.nav-pills:after { - display: table; - content: ""; - line-height: 0; -} -.nav-tabs:after, -.nav-pills:after { - clear: both; -} -.nav-tabs > li, -.nav-pills > li { - float: left; -} -.nav-tabs > li > a, -.nav-pills > li > a { - padding-right: 12px; - padding-left: 12px; - margin-right: 2px; - line-height: 14px; -} -.nav-tabs { - border-bottom: 1px solid #ddd; -} -.nav-tabs > li { - margin-bottom: -1px; -} -.nav-tabs > li > a { - padding-top: 8px; - padding-bottom: 8px; - line-height: 20px; - border: 1px solid transparent; - -webkit-border-radius: 4px 4px 0 0; - -moz-border-radius: 4px 4px 0 0; - border-radius: 4px 4px 0 0; -} -.nav-tabs > li > a:hover, -.nav-tabs > li > a:focus { - border-color: #eeeeee #eeeeee #dddddd; -} -.nav-tabs > .active > a, -.nav-tabs > .active > a:hover, -.nav-tabs > .active > a:focus { - color: #555555; - background-color: #ffffff; - border: 1px solid #ddd; - border-bottom-color: transparent; - cursor: default; -} -.nav-pills > li > a { - padding-top: 8px; - padding-bottom: 8px; - margin-top: 2px; - margin-bottom: 2px; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; -} -.nav-pills > .active > a, -.nav-pills > .active > a:hover, -.nav-pills > .active > a:focus { - color: #ffffff; - background-color: #0088cc; -} -.nav-stacked > li { - float: none; -} -.nav-stacked > li > a { - margin-right: 0; -} -.nav-tabs.nav-stacked { - border-bottom: 0; -} -.nav-tabs.nav-stacked > li > a { - border: 1px solid #ddd; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.nav-tabs.nav-stacked > li:first-child > a { - -webkit-border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; - border-top-right-radius: 4px; - -webkit-border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; - border-top-left-radius: 4px; -} -.nav-tabs.nav-stacked > li:last-child > a { - -webkit-border-bottom-right-radius: 4px; - -moz-border-radius-bottomright: 4px; - border-bottom-right-radius: 4px; - -webkit-border-bottom-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - border-bottom-left-radius: 4px; -} -.nav-tabs.nav-stacked > li > a:hover, -.nav-tabs.nav-stacked > li > a:focus { - border-color: #ddd; - z-index: 2; -} -.nav-pills.nav-stacked > li > a { - margin-bottom: 3px; -} -.nav-pills.nav-stacked > li:last-child > a { - margin-bottom: 1px; -} -.nav-tabs .dropdown-menu { - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; -} -.nav-pills .dropdown-menu { - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} -.nav .dropdown-toggle .caret { - border-top-color: #0088cc; - border-bottom-color: #0088cc; - margin-top: 6px; -} -.nav .dropdown-toggle:hover .caret, -.nav .dropdown-toggle:focus .caret { - border-top-color: #005580; - border-bottom-color: #005580; -} -/* move down carets for tabs */ -.nav-tabs .dropdown-toggle .caret { - margin-top: 8px; -} -.nav .active .dropdown-toggle .caret { - border-top-color: #fff; - border-bottom-color: #fff; -} -.nav-tabs .active .dropdown-toggle .caret { - border-top-color: #555555; - border-bottom-color: #555555; -} -.nav > .dropdown.active > a:hover, -.nav > .dropdown.active > a:focus { - cursor: pointer; -} -.nav-tabs .open .dropdown-toggle, -.nav-pills .open .dropdown-toggle, -.nav > li.dropdown.open.active > a:hover, -.nav > li.dropdown.open.active > a:focus { - color: #ffffff; - background-color: #999999; - border-color: #999999; -} -.nav li.dropdown.open .caret, -.nav li.dropdown.open.active .caret, -.nav li.dropdown.open a:hover .caret, -.nav li.dropdown.open a:focus .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; - opacity: 1; - filter: alpha(opacity=100); -} -.tabs-stacked .open > a:hover, -.tabs-stacked .open > a:focus { - border-color: #999999; -} -.tabbable { - *zoom: 1; -} -.tabbable:before, -.tabbable:after { - display: table; - content: ""; - line-height: 0; -} -.tabbable:after { - clear: both; -} -.tab-content { - overflow: auto; -} -.tabs-below > .nav-tabs, -.tabs-right > .nav-tabs, -.tabs-left > .nav-tabs { - border-bottom: 0; -} -.tab-content > .tab-pane, -.pill-content > .pill-pane { - display: none; -} -.tab-content > .active, -.pill-content > .active { - display: block; -} -.tabs-below > .nav-tabs { - border-top: 1px solid #ddd; -} -.tabs-below > .nav-tabs > li { - margin-top: -1px; - margin-bottom: 0; -} -.tabs-below > .nav-tabs > li > a { - -webkit-border-radius: 0 0 4px 4px; - -moz-border-radius: 0 0 4px 4px; - border-radius: 0 0 4px 4px; -} -.tabs-below > .nav-tabs > li > a:hover, -.tabs-below > .nav-tabs > li > a:focus { - border-bottom-color: transparent; - border-top-color: #ddd; -} -.tabs-below > .nav-tabs > .active > a, -.tabs-below > .nav-tabs > .active > a:hover, -.tabs-below > .nav-tabs > .active > a:focus { - border-color: transparent #ddd #ddd #ddd; -} -.tabs-left > .nav-tabs > li, -.tabs-right > .nav-tabs > li { - float: none; -} -.tabs-left > .nav-tabs > li > a, -.tabs-right > .nav-tabs > li > a { - min-width: 74px; - margin-right: 0; - margin-bottom: 3px; -} -.tabs-left > .nav-tabs { - float: left; - margin-right: 19px; - border-right: 1px solid #ddd; -} -.tabs-left > .nav-tabs > li > a { - margin-right: -1px; - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} -.tabs-left > .nav-tabs > li > a:hover, -.tabs-left > .nav-tabs > li > a:focus { - border-color: #eeeeee #dddddd #eeeeee #eeeeee; -} -.tabs-left > .nav-tabs .active > a, -.tabs-left > .nav-tabs .active > a:hover, -.tabs-left > .nav-tabs .active > a:focus { - border-color: #ddd transparent #ddd #ddd; - *border-right-color: #ffffff; -} -.tabs-right > .nav-tabs { - float: right; - margin-left: 19px; - border-left: 1px solid #ddd; -} -.tabs-right > .nav-tabs > li > a { - margin-left: -1px; - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} -.tabs-right > .nav-tabs > li > a:hover, -.tabs-right > .nav-tabs > li > a:focus { - border-color: #eeeeee #eeeeee #eeeeee #dddddd; -} -.tabs-right > .nav-tabs .active > a, -.tabs-right > .nav-tabs .active > a:hover, -.tabs-right > .nav-tabs .active > a:focus { - border-color: #ddd #ddd #ddd transparent; - *border-left-color: #ffffff; -} -.nav > .disabled > a { - color: #999999; -} -.nav > .disabled > a:hover, -.nav > .disabled > a:focus { - text-decoration: none; - background-color: transparent; - cursor: default; -} -.navbar { - overflow: visible; - margin-bottom: 20px; - *position: relative; - *z-index: 2; -} -.navbar-inner { - min-height: 40px; - padding-left: 20px; - padding-right: 20px; - background-color: #fafafa; - background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2)); - background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2); - background-image: -o-linear-gradient(top, #ffffff, #f2f2f2); - background-image: linear-gradient(to bottom, #ffffff, #f2f2f2); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0); - border: 1px solid #d4d4d4; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); - -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); - *zoom: 1; -} -.navbar-inner:before, -.navbar-inner:after { - display: table; - content: ""; - line-height: 0; -} -.navbar-inner:after { - clear: both; -} -.navbar .container { - width: auto; -} -.nav-collapse.collapse { - height: auto; - overflow: visible; -} -.navbar .brand { - float: left; - display: block; - padding: 10px 20px 10px; - margin-left: -20px; - font-size: 20px; - font-weight: 200; - color: #777777; - text-shadow: 0 1px 0 #ffffff; -} -.navbar .brand:hover, -.navbar .brand:focus { - text-decoration: none; -} -.navbar-text { - margin-bottom: 0; - line-height: 40px; - color: #777777; -} -.navbar-link { - color: #777777; -} -.navbar-link:hover, -.navbar-link:focus { - color: #333333; -} -.navbar .divider-vertical { - height: 40px; - margin: 0 9px; - border-left: 1px solid #f2f2f2; - border-right: 1px solid #ffffff; -} -.navbar .btn, -.navbar .btn-group { - margin-top: 5px; -} -.navbar .btn-group .btn, -.navbar .input-prepend .btn, -.navbar .input-append .btn, -.navbar .input-prepend .btn-group, -.navbar .input-append .btn-group { - margin-top: 0; -} -.navbar-form { - margin-bottom: 0; - *zoom: 1; -} -.navbar-form:before, -.navbar-form:after { - display: table; - content: ""; - line-height: 0; -} -.navbar-form:after { - clear: both; -} -.navbar-form input, -.navbar-form select, -.navbar-form .radio, -.navbar-form .checkbox { - margin-top: 5px; -} -.navbar-form input, -.navbar-form select, -.navbar-form .btn { - display: inline-block; - margin-bottom: 0; -} -.navbar-form input[type="image"], -.navbar-form input[type="checkbox"], -.navbar-form input[type="radio"] { - margin-top: 3px; -} -.navbar-form .input-append, -.navbar-form .input-prepend { - margin-top: 5px; - white-space: nowrap; -} -.navbar-form .input-append input, -.navbar-form .input-prepend input { - margin-top: 0; -} -.navbar-search { - position: relative; - float: left; - margin-top: 5px; - margin-bottom: 0; -} -.navbar-search .search-query { - margin-bottom: 0; - padding: 4px 14px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 13px; - font-weight: normal; - line-height: 1; - -webkit-border-radius: 15px; - -moz-border-radius: 15px; - border-radius: 15px; -} -.navbar-static-top { - position: static; - margin-bottom: 0; -} -.navbar-static-top .navbar-inner { - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.navbar-fixed-top, -.navbar-fixed-bottom { - position: fixed; - right: 0; - left: 0; - z-index: 1030; - margin-bottom: 0; -} -.navbar-fixed-top .navbar-inner, -.navbar-static-top .navbar-inner { - border-width: 0 0 1px; -} -.navbar-fixed-bottom .navbar-inner { - border-width: 1px 0 0; -} -.navbar-fixed-top .navbar-inner, -.navbar-fixed-bottom .navbar-inner { - padding-left: 0; - padding-right: 0; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.navbar-static-top .container, -.navbar-fixed-top .container, -.navbar-fixed-bottom .container { - width: 940px; -} -.navbar-fixed-top { - top: 0; -} -.navbar-fixed-top .navbar-inner, -.navbar-static-top .navbar-inner { - -webkit-box-shadow: 0 1px 10px rgba(0,0,0,.1); - -moz-box-shadow: 0 1px 10px rgba(0,0,0,.1); - box-shadow: 0 1px 10px rgba(0,0,0,.1); -} -.navbar-fixed-bottom { - bottom: 0; -} -.navbar-fixed-bottom .navbar-inner { - -webkit-box-shadow: 0 -1px 10px rgba(0,0,0,.1); - -moz-box-shadow: 0 -1px 10px rgba(0,0,0,.1); - box-shadow: 0 -1px 10px rgba(0,0,0,.1); -} -.navbar .nav { - position: relative; - left: 0; - display: block; - float: left; - margin: 0 10px 0 0; -} -.navbar .nav.pull-right { - float: right; - margin-right: 0; -} -.navbar .nav > li { - float: left; -} -.navbar .nav > li > a { - float: none; - padding: 10px 15px 10px; - color: #777777; - text-decoration: none; - text-shadow: 0 1px 0 #ffffff; -} -.navbar .nav .dropdown-toggle .caret { - margin-top: 8px; -} -.navbar .nav > li > a:focus, -.navbar .nav > li > a:hover { - background-color: transparent; - color: #333333; - text-decoration: none; -} -.navbar .nav > .active > a, -.navbar .nav > .active > a:hover, -.navbar .nav > .active > a:focus { - color: #555555; - text-decoration: none; - background-color: #e5e5e5; - -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); - -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); - box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); -} -.navbar .btn-navbar { - display: none; - float: right; - padding: 7px 10px; - margin-left: 5px; - margin-right: 5px; - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #ededed; - background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5)); - background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5); - background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5); - background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0); - border-color: #e5e5e5 #e5e5e5 #bfbfbf; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #e5e5e5; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); - -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); - box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); -} -.navbar .btn-navbar:hover, -.navbar .btn-navbar:focus, -.navbar .btn-navbar:active, -.navbar .btn-navbar.active, -.navbar .btn-navbar.disabled, -.navbar .btn-navbar[disabled] { - color: #ffffff; - background-color: #e5e5e5; - *background-color: #d9d9d9; -} -.navbar .btn-navbar:active, -.navbar .btn-navbar.active { - background-color: #cccccc \9; -} -.navbar .btn-navbar .icon-bar { - display: block; - width: 18px; - height: 2px; - background-color: #f5f5f5; - -webkit-border-radius: 1px; - -moz-border-radius: 1px; - border-radius: 1px; - -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); - -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); -} -.btn-navbar .icon-bar + .icon-bar { - margin-top: 3px; -} -.navbar .nav > li > .dropdown-menu:before { - content: ''; - display: inline-block; - border-left: 7px solid transparent; - border-right: 7px solid transparent; - border-bottom: 7px solid #ccc; - border-bottom-color: rgba(0, 0, 0, 0.2); - position: absolute; - top: -7px; - left: 9px; -} -.navbar .nav > li > .dropdown-menu:after { - content: ''; - display: inline-block; - border-left: 6px solid transparent; - border-right: 6px solid transparent; - border-bottom: 6px solid #ffffff; - position: absolute; - top: -6px; - left: 10px; -} -.navbar-fixed-bottom .nav > li > .dropdown-menu:before { - border-top: 7px solid #ccc; - border-top-color: rgba(0, 0, 0, 0.2); - border-bottom: 0; - bottom: -7px; - top: auto; -} -.navbar-fixed-bottom .nav > li > .dropdown-menu:after { - border-top: 6px solid #ffffff; - border-bottom: 0; - bottom: -6px; - top: auto; -} -.navbar .nav li.dropdown > a:hover .caret, -.navbar .nav li.dropdown > a:focus .caret { - border-top-color: #333333; - border-bottom-color: #333333; -} -.navbar .nav li.dropdown.open > .dropdown-toggle, -.navbar .nav li.dropdown.active > .dropdown-toggle, -.navbar .nav li.dropdown.open.active > .dropdown-toggle { - background-color: #e5e5e5; - color: #555555; -} -.navbar .nav li.dropdown > .dropdown-toggle .caret { - border-top-color: #777777; - border-bottom-color: #777777; -} -.navbar .nav li.dropdown.open > .dropdown-toggle .caret, -.navbar .nav li.dropdown.active > .dropdown-toggle .caret, -.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { - border-top-color: #555555; - border-bottom-color: #555555; -} -.navbar .pull-right > li > .dropdown-menu, -.navbar .nav > li > .dropdown-menu.pull-right { - left: auto; - right: 0; -} -.navbar .pull-right > li > .dropdown-menu:before, -.navbar .nav > li > .dropdown-menu.pull-right:before { - left: auto; - right: 12px; -} -.navbar .pull-right > li > .dropdown-menu:after, -.navbar .nav > li > .dropdown-menu.pull-right:after { - left: auto; - right: 13px; -} -.navbar .pull-right > li > .dropdown-menu .dropdown-menu, -.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu { - left: auto; - right: 100%; - margin-left: 0; - margin-right: -1px; - -webkit-border-radius: 6px 0 6px 6px; - -moz-border-radius: 6px 0 6px 6px; - border-radius: 6px 0 6px 6px; -} -.navbar-inverse .navbar-inner { - background-color: #1b1b1b; - background-image: -moz-linear-gradient(top, #222222, #111111); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111)); - background-image: -webkit-linear-gradient(top, #222222, #111111); - background-image: -o-linear-gradient(top, #222222, #111111); - background-image: linear-gradient(to bottom, #222222, #111111); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0); - border-color: #252525; -} -.navbar-inverse .brand, -.navbar-inverse .nav > li > a { - color: #999999; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); -} -.navbar-inverse .brand:hover, -.navbar-inverse .nav > li > a:hover, -.navbar-inverse .brand:focus, -.navbar-inverse .nav > li > a:focus { - color: #ffffff; -} -.navbar-inverse .brand { - color: #999999; -} -.navbar-inverse .navbar-text { - color: #999999; -} -.navbar-inverse .nav > li > a:focus, -.navbar-inverse .nav > li > a:hover { - background-color: transparent; - color: #ffffff; -} -.navbar-inverse .nav .active > a, -.navbar-inverse .nav .active > a:hover, -.navbar-inverse .nav .active > a:focus { - color: #ffffff; - background-color: #111111; -} -.navbar-inverse .navbar-link { - color: #999999; -} -.navbar-inverse .navbar-link:hover, -.navbar-inverse .navbar-link:focus { - color: #ffffff; -} -.navbar-inverse .divider-vertical { - border-left-color: #111111; - border-right-color: #222222; -} -.navbar-inverse .nav li.dropdown.open > .dropdown-toggle, -.navbar-inverse .nav li.dropdown.active > .dropdown-toggle, -.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle { - background-color: #111111; - color: #ffffff; -} -.navbar-inverse .nav li.dropdown > a:hover .caret, -.navbar-inverse .nav li.dropdown > a:focus .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; -} -.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { - border-top-color: #999999; - border-bottom-color: #999999; -} -.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, -.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret, -.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; -} -.navbar-inverse .navbar-search .search-query { - color: #ffffff; - background-color: #515151; - border-color: #111111; - -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15); - -moz-box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15); - box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15); - -webkit-transition: none; - -moz-transition: none; - -o-transition: none; - transition: none; -} -.navbar-inverse .navbar-search .search-query:-moz-placeholder { - color: #cccccc; -} -.navbar-inverse .navbar-search .search-query:-ms-input-placeholder { - color: #cccccc; -} -.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder { - color: #cccccc; -} -.navbar-inverse .navbar-search .search-query:focus, -.navbar-inverse .navbar-search .search-query.focused { - padding: 5px 15px; - color: #333333; - text-shadow: 0 1px 0 #ffffff; - background-color: #ffffff; - border: 0; - -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); - -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); - box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); - outline: 0; -} -.navbar-inverse .btn-navbar { - color: #ffffff; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #0e0e0e; - background-image: -moz-linear-gradient(top, #151515, #040404); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404)); - background-image: -webkit-linear-gradient(top, #151515, #040404); - background-image: -o-linear-gradient(top, #151515, #040404); - background-image: linear-gradient(to bottom, #151515, #040404); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0); - border-color: #040404 #040404 #000000; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - *background-color: #040404; - /* Darken IE7 buttons by default so they stand out more given they won't have borders */ - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.navbar-inverse .btn-navbar:hover, -.navbar-inverse .btn-navbar:focus, -.navbar-inverse .btn-navbar:active, -.navbar-inverse .btn-navbar.active, -.navbar-inverse .btn-navbar.disabled, -.navbar-inverse .btn-navbar[disabled] { - color: #ffffff; - background-color: #040404; - *background-color: #000000; -} -.navbar-inverse .btn-navbar:active, -.navbar-inverse .btn-navbar.active { - background-color: #000000 \9; -} -.breadcrumb { - padding: 8px 15px; - margin: 0 0 20px; - list-style: none; - background-color: #f5f5f5; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.breadcrumb > li { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - *zoom: 1; - text-shadow: 0 1px 0 #ffffff; -} -.breadcrumb > li > .divider { - padding: 0 5px; - color: #ccc; -} -.breadcrumb > .active { - color: #999999; -} -.pagination { - margin: 20px 0; -} -.pagination ul { - display: inline-block; - *display: inline; - /* IE7 inline-block hack */ - *zoom: 1; - margin-left: 0; - margin-bottom: 0; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); -} -.pagination ul > li { - display: inline; -} -.pagination ul > li > a, -.pagination ul > li > span { - float: left; - padding: 4px 12px; - line-height: 20px; - text-decoration: none; - background-color: #ffffff; - border: 1px solid #dddddd; - border-left-width: 0; -} -.pagination ul > li > a:hover, -.pagination ul > li > a:focus, -.pagination ul > .active > a, -.pagination ul > .active > span { - background-color: #f5f5f5; -} -.pagination ul > .active > a, -.pagination ul > .active > span { - color: #999999; - cursor: default; -} -.pagination ul > .disabled > span, -.pagination ul > .disabled > a, -.pagination ul > .disabled > a:hover, -.pagination ul > .disabled > a:focus { - color: #999999; - background-color: transparent; - cursor: default; -} -.pagination ul > li:first-child > a, -.pagination ul > li:first-child > span { - border-left-width: 1px; - -webkit-border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; - border-top-left-radius: 4px; - -webkit-border-bottom-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - border-bottom-left-radius: 4px; -} -.pagination ul > li:last-child > a, -.pagination ul > li:last-child > span { - -webkit-border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; - border-top-right-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - -moz-border-radius-bottomright: 4px; - border-bottom-right-radius: 4px; -} -.pagination-centered { - text-align: center; -} -.pagination-right { - text-align: right; -} -.pagination-large ul > li > a, -.pagination-large ul > li > span { - padding: 11px 19px; - font-size: 17.5px; -} -.pagination-large ul > li:first-child > a, -.pagination-large ul > li:first-child > span { - -webkit-border-top-left-radius: 6px; - -moz-border-radius-topleft: 6px; - border-top-left-radius: 6px; - -webkit-border-bottom-left-radius: 6px; - -moz-border-radius-bottomleft: 6px; - border-bottom-left-radius: 6px; -} -.pagination-large ul > li:last-child > a, -.pagination-large ul > li:last-child > span { - -webkit-border-top-right-radius: 6px; - -moz-border-radius-topright: 6px; - border-top-right-radius: 6px; - -webkit-border-bottom-right-radius: 6px; - -moz-border-radius-bottomright: 6px; - border-bottom-right-radius: 6px; -} -.pagination-mini ul > li:first-child > a, -.pagination-small ul > li:first-child > a, -.pagination-mini ul > li:first-child > span, -.pagination-small ul > li:first-child > span { - -webkit-border-top-left-radius: 3px; - -moz-border-radius-topleft: 3px; - border-top-left-radius: 3px; - -webkit-border-bottom-left-radius: 3px; - -moz-border-radius-bottomleft: 3px; - border-bottom-left-radius: 3px; -} -.pagination-mini ul > li:last-child > a, -.pagination-small ul > li:last-child > a, -.pagination-mini ul > li:last-child > span, -.pagination-small ul > li:last-child > span { - -webkit-border-top-right-radius: 3px; - -moz-border-radius-topright: 3px; - border-top-right-radius: 3px; - -webkit-border-bottom-right-radius: 3px; - -moz-border-radius-bottomright: 3px; - border-bottom-right-radius: 3px; -} -.pagination-small ul > li > a, -.pagination-small ul > li > span { - padding: 2px 10px; - font-size: 11.9px; -} -.pagination-mini ul > li > a, -.pagination-mini ul > li > span { - padding: 0 6px; - font-size: 10.5px; -} -.pager { - margin: 20px 0; - list-style: none; - text-align: center; - *zoom: 1; -} -.pager:before, -.pager:after { - display: table; - content: ""; - line-height: 0; -} -.pager:after { - clear: both; -} -.pager li { - display: inline; -} -.pager li > a, -.pager li > span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - -webkit-border-radius: 15px; - -moz-border-radius: 15px; - border-radius: 15px; -} -.pager li > a:hover, -.pager li > a:focus { - text-decoration: none; - background-color: #f5f5f5; -} -.pager .next > a, -.pager .next > span { - float: right; -} -.pager .previous > a, -.pager .previous > span { - float: left; -} -.pager .disabled > a, -.pager .disabled > a:hover, -.pager .disabled > a:focus, -.pager .disabled > span { - color: #999999; - background-color: #fff; - cursor: default; -} -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000000; -} -.modal-backdrop.fade { - opacity: 0; -} -.modal-backdrop, -.modal-backdrop.fade.in { - opacity: 0.8; - filter: alpha(opacity=80); -} -.modal { - position: fixed; - top: 10%; - left: 50%; - z-index: 1050; - width: 560px; - margin-left: -280px; - background-color: #ffffff; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, 0.3); - *border: 1px solid #999; - /* IE6-7 */ - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - -webkit-background-clip: padding-box; - -moz-background-clip: padding-box; - background-clip: padding-box; - outline: none; -} -.modal.fade { - -webkit-transition: opacity .3s linear, top .3s ease-out; - -moz-transition: opacity .3s linear, top .3s ease-out; - -o-transition: opacity .3s linear, top .3s ease-out; - transition: opacity .3s linear, top .3s ease-out; - top: -25%; -} -.modal.fade.in { - top: 10%; -} -.modal-header { - padding: 9px 15px; - border-bottom: 1px solid #eee; -} -.modal-header .close { - margin-top: 2px; -} -.modal-header h3 { - margin: 0; - line-height: 30px; -} -.modal-body { - position: relative; - overflow-y: auto; - max-height: 400px; - padding: 15px; -} -.modal-form { - margin-bottom: 0; -} -.modal-footer { - padding: 14px 15px 15px; - margin-bottom: 0; - text-align: right; - background-color: #f5f5f5; - border-top: 1px solid #ddd; - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; - -webkit-box-shadow: inset 0 1px 0 #ffffff; - -moz-box-shadow: inset 0 1px 0 #ffffff; - box-shadow: inset 0 1px 0 #ffffff; - *zoom: 1; -} -.modal-footer:before, -.modal-footer:after { - display: table; - content: ""; - line-height: 0; -} -.modal-footer:after { - clear: both; -} -.modal-footer .btn + .btn { - margin-left: 5px; - margin-bottom: 0; -} -.modal-footer .btn-group .btn + .btn { - margin-left: -1px; -} -.modal-footer .btn-block + .btn-block { - margin-left: 0; -} -.tooltip { - position: absolute; - z-index: 1030; - display: block; - visibility: visible; - font-size: 11px; - line-height: 1.4; - opacity: 0; - filter: alpha(opacity=0); -} -.tooltip.in { - opacity: 0.8; - filter: alpha(opacity=80); -} -.tooltip.top { - margin-top: -3px; - padding: 5px 0; -} -.tooltip.right { - margin-left: 3px; - padding: 0 5px; -} -.tooltip.bottom { - margin-top: 3px; - padding: 5px 0; -} -.tooltip.left { - margin-left: -3px; - padding: 0 5px; -} -.tooltip-inner { - max-width: 200px; - padding: 8px; - color: #ffffff; - text-align: center; - text-decoration: none; - background-color: #000000; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-width: 5px 5px 0; - border-top-color: #000000; -} -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-width: 5px 5px 5px 0; - border-right-color: #000000; -} -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-width: 5px 0 5px 5px; - border-left-color: #000000; -} -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000000; -} -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1010; - display: none; - max-width: 276px; - padding: 1px; - text-align: left; - background-color: #ffffff; - -webkit-background-clip: padding-box; - -moz-background-clip: padding; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - white-space: normal; -} -.popover.top { - margin-top: -10px; -} -.popover.right { - margin-left: 10px; -} -.popover.bottom { - margin-top: 10px; -} -.popover.left { - margin-left: -10px; -} -.popover-title { - margin: 0; - padding: 8px 14px; - font-size: 14px; - font-weight: normal; - line-height: 18px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - -webkit-border-radius: 5px 5px 0 0; - -moz-border-radius: 5px 5px 0 0; - border-radius: 5px 5px 0 0; -} -.popover-title:empty { - display: none; -} -.popover-content { - padding: 9px 14px; -} -.popover .arrow, -.popover .arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.popover .arrow { - border-width: 11px; -} -.popover .arrow:after { - border-width: 10px; - content: ""; -} -.popover.top .arrow { - left: 50%; - margin-left: -11px; - border-bottom-width: 0; - border-top-color: #999; - border-top-color: rgba(0, 0, 0, 0.25); - bottom: -11px; -} -.popover.top .arrow:after { - bottom: 1px; - margin-left: -10px; - border-bottom-width: 0; - border-top-color: #ffffff; -} -.popover.right .arrow { - top: 50%; - left: -11px; - margin-top: -11px; - border-left-width: 0; - border-right-color: #999; - border-right-color: rgba(0, 0, 0, 0.25); -} -.popover.right .arrow:after { - left: 1px; - bottom: -10px; - border-left-width: 0; - border-right-color: #ffffff; -} -.popover.bottom .arrow { - left: 50%; - margin-left: -11px; - border-top-width: 0; - border-bottom-color: #999; - border-bottom-color: rgba(0, 0, 0, 0.25); - top: -11px; -} -.popover.bottom .arrow:after { - top: 1px; - margin-left: -10px; - border-top-width: 0; - border-bottom-color: #ffffff; -} -.popover.left .arrow { - top: 50%; - right: -11px; - margin-top: -11px; - border-right-width: 0; - border-left-color: #999; - border-left-color: rgba(0, 0, 0, 0.25); -} -.popover.left .arrow:after { - right: 1px; - border-right-width: 0; - border-left-color: #ffffff; - bottom: -10px; -} -.thumbnails { - margin-left: -20px; - list-style: none; - *zoom: 1; -} -.thumbnails:before, -.thumbnails:after { - display: table; - content: ""; - line-height: 0; -} -.thumbnails:after { - clear: both; -} -.row-fluid .thumbnails { - margin-left: 0; -} -.thumbnails > li { - float: left; - margin-bottom: 20px; - margin-left: 20px; -} -.thumbnail { - display: block; - padding: 4px; - line-height: 20px; - border: 1px solid #ddd; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); - -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); - -webkit-transition: all 0.2s ease-in-out; - -moz-transition: all 0.2s ease-in-out; - -o-transition: all 0.2s ease-in-out; - transition: all 0.2s ease-in-out; -} -a.thumbnail:hover, -a.thumbnail:focus { - border-color: #0088cc; - -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); - -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); - box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); -} -.thumbnail > img { - display: block; - max-width: 100%; - margin-left: auto; - margin-right: auto; -} -.thumbnail .caption { - padding: 9px; - color: #555555; -} -.media, -.media-body { - overflow: hidden; - *overflow: visible; - zoom: 1; -} -.media, -.media .media { - margin-top: 15px; -} -.media:first-child { - margin-top: 0; -} -.media-object { - display: block; -} -.media-heading { - margin: 0 0 5px; -} -.media > .pull-left { - margin-right: 10px; -} -.media > .pull-right { - margin-left: 10px; -} -.media-list { - margin-left: 0; - list-style: none; -} -.label, -.badge { - display: inline-block; - padding: 2px 4px; - font-size: 11.844px; - font-weight: bold; - line-height: 14px; - color: #ffffff; - vertical-align: baseline; - white-space: nowrap; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #999999; -} -.label { - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -.badge { - padding-left: 9px; - padding-right: 9px; - -webkit-border-radius: 9px; - -moz-border-radius: 9px; - border-radius: 9px; -} -.label:empty, -.badge:empty { - display: none; -} -a.label:hover, -a.label:focus, -a.badge:hover, -a.badge:focus { - color: #ffffff; - text-decoration: none; - cursor: pointer; -} -.label-important, -.badge-important { - background-color: #b94a48; -} -.label-important[href], -.badge-important[href] { - background-color: #953b39; -} -.label-warning, -.badge-warning { - background-color: #f89406; -} -.label-warning[href], -.badge-warning[href] { - background-color: #c67605; -} -.label-success, -.badge-success { - background-color: #468847; -} -.label-success[href], -.badge-success[href] { - background-color: #356635; -} -.label-info, -.badge-info { - background-color: #3a87ad; -} -.label-info[href], -.badge-info[href] { - background-color: #2d6987; -} -.label-inverse, -.badge-inverse { - background-color: #333333; -} -.label-inverse[href], -.badge-inverse[href] { - background-color: #1a1a1a; -} -.btn .label, -.btn .badge { - position: relative; - top: -1px; -} -.btn-mini .label, -.btn-mini .badge { - top: 0; -} -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@-moz-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@-ms-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@-o-keyframes progress-bar-stripes { - from { - background-position: 0 0; - } - to { - background-position: 40px 0; - } -} -@keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -.progress { - overflow: hidden; - height: 20px; - margin-bottom: 20px; - background-color: #f7f7f7; - background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); - background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); - background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.progress .bar { - width: 0%; - height: 100%; - color: #ffffff; - float: left; - font-size: 12px; - text-align: center; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - background-color: #0e90d2; - background-image: -moz-linear-gradient(top, #149bdf, #0480be); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); - background-image: -webkit-linear-gradient(top, #149bdf, #0480be); - background-image: -o-linear-gradient(top, #149bdf, #0480be); - background-image: linear-gradient(to bottom, #149bdf, #0480be); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - -webkit-transition: width 0.6s ease; - -moz-transition: width 0.6s ease; - -o-transition: width 0.6s ease; - transition: width 0.6s ease; -} -.progress .bar + .bar { - -webkit-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); - -moz-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); - box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); -} -.progress-striped .bar { - background-color: #149bdf; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - -webkit-background-size: 40px 40px; - -moz-background-size: 40px 40px; - -o-background-size: 40px 40px; - background-size: 40px 40px; -} -.progress.active .bar { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -moz-animation: progress-bar-stripes 2s linear infinite; - -ms-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} -.progress-danger .bar, -.progress .bar-danger { - background-color: #dd514c; - background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); - background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); - background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); - background-image: linear-gradient(to bottom, #ee5f5b, #c43c35); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0); -} -.progress-danger.progress-striped .bar, -.progress-striped .bar-danger { - background-color: #ee5f5b; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} -.progress-success .bar, -.progress .bar-success { - background-color: #5eb95e; - background-image: -moz-linear-gradient(top, #62c462, #57a957); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); - background-image: -webkit-linear-gradient(top, #62c462, #57a957); - background-image: -o-linear-gradient(top, #62c462, #57a957); - background-image: linear-gradient(to bottom, #62c462, #57a957); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0); -} -.progress-success.progress-striped .bar, -.progress-striped .bar-success { - background-color: #62c462; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} -.progress-info .bar, -.progress .bar-info { - background-color: #4bb1cf; - background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); - background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); - background-image: -o-linear-gradient(top, #5bc0de, #339bb9); - background-image: linear-gradient(to bottom, #5bc0de, #339bb9); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0); -} -.progress-info.progress-striped .bar, -.progress-striped .bar-info { - background-color: #5bc0de; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} -.progress-warning .bar, -.progress .bar-warning { - background-color: #faa732; - background-image: -moz-linear-gradient(top, #fbb450, #f89406); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); - background-image: -webkit-linear-gradient(top, #fbb450, #f89406); - background-image: -o-linear-gradient(top, #fbb450, #f89406); - background-image: linear-gradient(to bottom, #fbb450, #f89406); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); -} -.progress-warning.progress-striped .bar, -.progress-striped .bar-warning { - background-color: #fbb450; - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -} -.accordion { - margin-bottom: 20px; -} -.accordion-group { - margin-bottom: 2px; - border: 1px solid #e5e5e5; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.accordion-heading { - border-bottom: 0; -} -.accordion-heading .accordion-toggle { - display: block; - padding: 8px 15px; -} -.accordion-toggle { - cursor: pointer; -} -.accordion-inner { - padding: 9px 15px; - border-top: 1px solid #e5e5e5; -} -.carousel { - position: relative; - margin-bottom: 20px; - line-height: 1; -} -.carousel-inner { - overflow: hidden; - width: 100%; - position: relative; -} -.carousel-inner > .item { - display: none; - position: relative; - -webkit-transition: 0.6s ease-in-out left; - -moz-transition: 0.6s ease-in-out left; - -o-transition: 0.6s ease-in-out left; - transition: 0.6s ease-in-out left; -} -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - display: block; - line-height: 1; -} -.carousel-inner > .active, -.carousel-inner > .next, -.carousel-inner > .prev { - display: block; -} -.carousel-inner > .active { - left: 0; -} -.carousel-inner > .next, -.carousel-inner > .prev { - position: absolute; - top: 0; - width: 100%; -} -.carousel-inner > .next { - left: 100%; -} -.carousel-inner > .prev { - left: -100%; -} -.carousel-inner > .next.left, -.carousel-inner > .prev.right { - left: 0; -} -.carousel-inner > .active.left { - left: -100%; -} -.carousel-inner > .active.right { - left: 100%; -} -.carousel-control { - position: absolute; - top: 40%; - left: 15px; - width: 40px; - height: 40px; - margin-top: -20px; - font-size: 60px; - font-weight: 100; - line-height: 30px; - color: #ffffff; - text-align: center; - background: #222222; - border: 3px solid #ffffff; - -webkit-border-radius: 23px; - -moz-border-radius: 23px; - border-radius: 23px; - opacity: 0.5; - filter: alpha(opacity=50); -} -.carousel-control.right { - left: auto; - right: 15px; -} -.carousel-control:hover, -.carousel-control:focus { - color: #ffffff; - text-decoration: none; - opacity: 0.9; - filter: alpha(opacity=90); -} -.carousel-indicators { - position: absolute; - top: 15px; - right: 15px; - z-index: 5; - margin: 0; - list-style: none; -} -.carousel-indicators li { - display: block; - float: left; - width: 10px; - height: 10px; - margin-left: 5px; - text-indent: -999px; - background-color: #ccc; - background-color: rgba(255, 255, 255, 0.25); - border-radius: 5px; -} -.carousel-indicators .active { - background-color: #fff; -} -.carousel-caption { - position: absolute; - left: 0; - right: 0; - bottom: 0; - padding: 15px; - background: #333333; - background: rgba(0, 0, 0, 0.75); -} -.carousel-caption h4, -.carousel-caption p { - color: #ffffff; - line-height: 20px; -} -.carousel-caption h4 { - margin: 0 0 5px; -} -.carousel-caption p { - margin-bottom: 0; -} -.hero-unit { - padding: 60px; - margin-bottom: 30px; - font-size: 18px; - font-weight: 200; - line-height: 30px; - color: inherit; - background-color: #eeeeee; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} -.hero-unit h1 { - margin-bottom: 0; - font-size: 60px; - line-height: 1; - color: inherit; - letter-spacing: -1px; -} -.hero-unit li { - line-height: 30px; -} -.pull-right { - float: right; -} -.pull-left { - float: left; -} -.hide { - display: none; -} -.show { - display: block; -} -.invisible { - visibility: hidden; -} -.affix { - position: fixed; -} - From fd8f628a4fb23e0e338880622916f26fe75870a2 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 17 Mar 2017 16:33:48 -0400 Subject: [PATCH 164/269] generate .css files and put them into the source directory --- .gitignore | 2 ++ openid-connect-server-webapp/pom.xml | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index cc7301fd52..a403cd3d80 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ bin /target .springBeans nb-configuration.xml +openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.css +openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.css diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index 141cf54bf8..68059590fd 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -88,7 +88,7 @@ lesscss-maven-plugin ${project.basedir}/src/main/less - ${project.build.directory}/${project.build.finalName}/resources/bootstrap2/css + ${project.basedir}/src/main/webapp/resources/bootstrap2/css false bootstrap.less @@ -100,6 +100,7 @@ compile + process-sources
    From 9b2177a4223563f68d6ddd8881828402ff76c90d Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 17 Mar 2017 17:52:33 -0400 Subject: [PATCH 165/269] =?UTF-8?q?switched=20to=20using=20wro4j,=20main?= =?UTF-8?q?=20file=20works=20but=20responsive=20file=20doesn=E2=80=99t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openid-connect-server-webapp/pom.xml | 33 +++++++++++-------- .../src/main/webapp/WEB-INF/wro.properties | 2 ++ .../src/main/webapp/WEB-INF/wro.xml | 6 ++++ .../src/main/{ => webapp}/less/accordion.less | 0 .../src/main/{ => webapp}/less/alerts.less | 0 .../less/bootstrap-responsive.less | 0 .../src/main/{ => webapp}/less/bootstrap.less | 0 .../main/{ => webapp}/less/breadcrumbs.less | 0 .../main/{ => webapp}/less/button-groups.less | 0 .../src/main/{ => webapp}/less/buttons.less | 0 .../src/main/{ => webapp}/less/carousel.less | 0 .../src/main/{ => webapp}/less/close.less | 0 .../src/main/{ => webapp}/less/code.less | 0 .../less/component-animations.less | 0 .../src/main/{ => webapp}/less/dropdowns.less | 0 .../src/main/{ => webapp}/less/forms.less | 0 .../src/main/{ => webapp}/less/grid.less | 0 .../src/main/{ => webapp}/less/hero-unit.less | 0 .../main/{ => webapp}/less/labels-badges.less | 0 .../src/main/{ => webapp}/less/layouts.less | 0 .../src/main/{ => webapp}/less/media.less | 0 .../src/main/{ => webapp}/less/mixins.less | 0 .../src/main/{ => webapp}/less/modals.less | 0 .../src/main/{ => webapp}/less/navbar.less | 0 .../src/main/{ => webapp}/less/navs.less | 0 .../src/main/{ => webapp}/less/pager.less | 0 .../main/{ => webapp}/less/pagination.less | 0 .../src/main/{ => webapp}/less/popovers.less | 0 .../main/{ => webapp}/less/progress-bars.less | 0 .../src/main/{ => webapp}/less/reset.less | 0 .../less/responsive-1200px-min.less | 0 .../less/responsive-767px-max.less | 0 .../less/responsive-768px-979px.less | 0 .../{ => webapp}/less/responsive-navbar.less | 0 .../less/responsive-utilities.less | 0 .../main/{ => webapp}/less/scaffolding.less | 0 .../src/main/{ => webapp}/less/sprites.less | 0 .../src/main/{ => webapp}/less/tables.less | 0 .../main/{ => webapp}/less/thumbnails.less | 0 .../src/main/{ => webapp}/less/tooltip.less | 0 .../src/main/{ => webapp}/less/type.less | 0 .../src/main/{ => webapp}/less/utilities.less | 0 .../src/main/{ => webapp}/less/variables.less | 0 .../src/main/{ => webapp}/less/wells.less | 0 44 files changed, 27 insertions(+), 14 deletions(-) create mode 100644 openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.properties create mode 100644 openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.xml rename openid-connect-server-webapp/src/main/{ => webapp}/less/accordion.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/alerts.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/bootstrap-responsive.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/bootstrap.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/breadcrumbs.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/button-groups.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/buttons.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/carousel.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/close.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/code.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/component-animations.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/dropdowns.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/forms.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/grid.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/hero-unit.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/labels-badges.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/layouts.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/media.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/mixins.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/modals.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/navbar.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/navs.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/pager.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/pagination.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/popovers.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/progress-bars.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/reset.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/responsive-1200px-min.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/responsive-767px-max.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/responsive-768px-979px.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/responsive-navbar.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/responsive-utilities.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/scaffolding.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/sprites.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/tables.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/thumbnails.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/tooltip.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/type.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/utilities.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/variables.less (100%) rename openid-connect-server-webapp/src/main/{ => webapp}/less/wells.less (100%) diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index 68059590fd..b3eaa31b60 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -84,30 +84,35 @@ - org.lesscss - lesscss-maven-plugin - - ${project.basedir}/src/main/less - ${project.basedir}/src/main/webapp/resources/bootstrap2/css - false - - bootstrap.less - bootstrap-responsive.less - - + ro.isdc.wro4j + wro4j-maven-plugin + 1.8.0 + compile - compile + run - process-sources + + bootstrap + ${project.build.directory}/${project.build.finalName} + ${project.build.directory}/${project.build.finalName}/resources/bootstrap2/css/ + ${project.build.directory}/${project.build.finalName}/js/ + ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory + +
    - + + ro.isdc.wro4j + wro4j-extensions + 1.8.0 + + org.mitre openid-connect-server diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.properties b/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.properties new file mode 100644 index 0000000000..8b8c9dd890 --- /dev/null +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.properties @@ -0,0 +1,2 @@ +preProcessors=cssImport +postProcessors=lessCss diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.xml new file mode 100644 index 0000000000..a69a55bb9f --- /dev/null +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.xml @@ -0,0 +1,6 @@ + + + /less/bootstrap.less + /less/bootstrap-responsive.less + + \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/less/accordion.less b/openid-connect-server-webapp/src/main/webapp/less/accordion.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/accordion.less rename to openid-connect-server-webapp/src/main/webapp/less/accordion.less diff --git a/openid-connect-server-webapp/src/main/less/alerts.less b/openid-connect-server-webapp/src/main/webapp/less/alerts.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/alerts.less rename to openid-connect-server-webapp/src/main/webapp/less/alerts.less diff --git a/openid-connect-server-webapp/src/main/less/bootstrap-responsive.less b/openid-connect-server-webapp/src/main/webapp/less/bootstrap-responsive.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/bootstrap-responsive.less rename to openid-connect-server-webapp/src/main/webapp/less/bootstrap-responsive.less diff --git a/openid-connect-server-webapp/src/main/less/bootstrap.less b/openid-connect-server-webapp/src/main/webapp/less/bootstrap.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/bootstrap.less rename to openid-connect-server-webapp/src/main/webapp/less/bootstrap.less diff --git a/openid-connect-server-webapp/src/main/less/breadcrumbs.less b/openid-connect-server-webapp/src/main/webapp/less/breadcrumbs.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/breadcrumbs.less rename to openid-connect-server-webapp/src/main/webapp/less/breadcrumbs.less diff --git a/openid-connect-server-webapp/src/main/less/button-groups.less b/openid-connect-server-webapp/src/main/webapp/less/button-groups.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/button-groups.less rename to openid-connect-server-webapp/src/main/webapp/less/button-groups.less diff --git a/openid-connect-server-webapp/src/main/less/buttons.less b/openid-connect-server-webapp/src/main/webapp/less/buttons.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/buttons.less rename to openid-connect-server-webapp/src/main/webapp/less/buttons.less diff --git a/openid-connect-server-webapp/src/main/less/carousel.less b/openid-connect-server-webapp/src/main/webapp/less/carousel.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/carousel.less rename to openid-connect-server-webapp/src/main/webapp/less/carousel.less diff --git a/openid-connect-server-webapp/src/main/less/close.less b/openid-connect-server-webapp/src/main/webapp/less/close.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/close.less rename to openid-connect-server-webapp/src/main/webapp/less/close.less diff --git a/openid-connect-server-webapp/src/main/less/code.less b/openid-connect-server-webapp/src/main/webapp/less/code.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/code.less rename to openid-connect-server-webapp/src/main/webapp/less/code.less diff --git a/openid-connect-server-webapp/src/main/less/component-animations.less b/openid-connect-server-webapp/src/main/webapp/less/component-animations.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/component-animations.less rename to openid-connect-server-webapp/src/main/webapp/less/component-animations.less diff --git a/openid-connect-server-webapp/src/main/less/dropdowns.less b/openid-connect-server-webapp/src/main/webapp/less/dropdowns.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/dropdowns.less rename to openid-connect-server-webapp/src/main/webapp/less/dropdowns.less diff --git a/openid-connect-server-webapp/src/main/less/forms.less b/openid-connect-server-webapp/src/main/webapp/less/forms.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/forms.less rename to openid-connect-server-webapp/src/main/webapp/less/forms.less diff --git a/openid-connect-server-webapp/src/main/less/grid.less b/openid-connect-server-webapp/src/main/webapp/less/grid.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/grid.less rename to openid-connect-server-webapp/src/main/webapp/less/grid.less diff --git a/openid-connect-server-webapp/src/main/less/hero-unit.less b/openid-connect-server-webapp/src/main/webapp/less/hero-unit.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/hero-unit.less rename to openid-connect-server-webapp/src/main/webapp/less/hero-unit.less diff --git a/openid-connect-server-webapp/src/main/less/labels-badges.less b/openid-connect-server-webapp/src/main/webapp/less/labels-badges.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/labels-badges.less rename to openid-connect-server-webapp/src/main/webapp/less/labels-badges.less diff --git a/openid-connect-server-webapp/src/main/less/layouts.less b/openid-connect-server-webapp/src/main/webapp/less/layouts.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/layouts.less rename to openid-connect-server-webapp/src/main/webapp/less/layouts.less diff --git a/openid-connect-server-webapp/src/main/less/media.less b/openid-connect-server-webapp/src/main/webapp/less/media.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/media.less rename to openid-connect-server-webapp/src/main/webapp/less/media.less diff --git a/openid-connect-server-webapp/src/main/less/mixins.less b/openid-connect-server-webapp/src/main/webapp/less/mixins.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/mixins.less rename to openid-connect-server-webapp/src/main/webapp/less/mixins.less diff --git a/openid-connect-server-webapp/src/main/less/modals.less b/openid-connect-server-webapp/src/main/webapp/less/modals.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/modals.less rename to openid-connect-server-webapp/src/main/webapp/less/modals.less diff --git a/openid-connect-server-webapp/src/main/less/navbar.less b/openid-connect-server-webapp/src/main/webapp/less/navbar.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/navbar.less rename to openid-connect-server-webapp/src/main/webapp/less/navbar.less diff --git a/openid-connect-server-webapp/src/main/less/navs.less b/openid-connect-server-webapp/src/main/webapp/less/navs.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/navs.less rename to openid-connect-server-webapp/src/main/webapp/less/navs.less diff --git a/openid-connect-server-webapp/src/main/less/pager.less b/openid-connect-server-webapp/src/main/webapp/less/pager.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/pager.less rename to openid-connect-server-webapp/src/main/webapp/less/pager.less diff --git a/openid-connect-server-webapp/src/main/less/pagination.less b/openid-connect-server-webapp/src/main/webapp/less/pagination.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/pagination.less rename to openid-connect-server-webapp/src/main/webapp/less/pagination.less diff --git a/openid-connect-server-webapp/src/main/less/popovers.less b/openid-connect-server-webapp/src/main/webapp/less/popovers.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/popovers.less rename to openid-connect-server-webapp/src/main/webapp/less/popovers.less diff --git a/openid-connect-server-webapp/src/main/less/progress-bars.less b/openid-connect-server-webapp/src/main/webapp/less/progress-bars.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/progress-bars.less rename to openid-connect-server-webapp/src/main/webapp/less/progress-bars.less diff --git a/openid-connect-server-webapp/src/main/less/reset.less b/openid-connect-server-webapp/src/main/webapp/less/reset.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/reset.less rename to openid-connect-server-webapp/src/main/webapp/less/reset.less diff --git a/openid-connect-server-webapp/src/main/less/responsive-1200px-min.less b/openid-connect-server-webapp/src/main/webapp/less/responsive-1200px-min.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/responsive-1200px-min.less rename to openid-connect-server-webapp/src/main/webapp/less/responsive-1200px-min.less diff --git a/openid-connect-server-webapp/src/main/less/responsive-767px-max.less b/openid-connect-server-webapp/src/main/webapp/less/responsive-767px-max.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/responsive-767px-max.less rename to openid-connect-server-webapp/src/main/webapp/less/responsive-767px-max.less diff --git a/openid-connect-server-webapp/src/main/less/responsive-768px-979px.less b/openid-connect-server-webapp/src/main/webapp/less/responsive-768px-979px.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/responsive-768px-979px.less rename to openid-connect-server-webapp/src/main/webapp/less/responsive-768px-979px.less diff --git a/openid-connect-server-webapp/src/main/less/responsive-navbar.less b/openid-connect-server-webapp/src/main/webapp/less/responsive-navbar.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/responsive-navbar.less rename to openid-connect-server-webapp/src/main/webapp/less/responsive-navbar.less diff --git a/openid-connect-server-webapp/src/main/less/responsive-utilities.less b/openid-connect-server-webapp/src/main/webapp/less/responsive-utilities.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/responsive-utilities.less rename to openid-connect-server-webapp/src/main/webapp/less/responsive-utilities.less diff --git a/openid-connect-server-webapp/src/main/less/scaffolding.less b/openid-connect-server-webapp/src/main/webapp/less/scaffolding.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/scaffolding.less rename to openid-connect-server-webapp/src/main/webapp/less/scaffolding.less diff --git a/openid-connect-server-webapp/src/main/less/sprites.less b/openid-connect-server-webapp/src/main/webapp/less/sprites.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/sprites.less rename to openid-connect-server-webapp/src/main/webapp/less/sprites.less diff --git a/openid-connect-server-webapp/src/main/less/tables.less b/openid-connect-server-webapp/src/main/webapp/less/tables.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/tables.less rename to openid-connect-server-webapp/src/main/webapp/less/tables.less diff --git a/openid-connect-server-webapp/src/main/less/thumbnails.less b/openid-connect-server-webapp/src/main/webapp/less/thumbnails.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/thumbnails.less rename to openid-connect-server-webapp/src/main/webapp/less/thumbnails.less diff --git a/openid-connect-server-webapp/src/main/less/tooltip.less b/openid-connect-server-webapp/src/main/webapp/less/tooltip.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/tooltip.less rename to openid-connect-server-webapp/src/main/webapp/less/tooltip.less diff --git a/openid-connect-server-webapp/src/main/less/type.less b/openid-connect-server-webapp/src/main/webapp/less/type.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/type.less rename to openid-connect-server-webapp/src/main/webapp/less/type.less diff --git a/openid-connect-server-webapp/src/main/less/utilities.less b/openid-connect-server-webapp/src/main/webapp/less/utilities.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/utilities.less rename to openid-connect-server-webapp/src/main/webapp/less/utilities.less diff --git a/openid-connect-server-webapp/src/main/less/variables.less b/openid-connect-server-webapp/src/main/webapp/less/variables.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/variables.less rename to openid-connect-server-webapp/src/main/webapp/less/variables.less diff --git a/openid-connect-server-webapp/src/main/less/wells.less b/openid-connect-server-webapp/src/main/webapp/less/wells.less similarity index 100% rename from openid-connect-server-webapp/src/main/less/wells.less rename to openid-connect-server-webapp/src/main/webapp/less/wells.less From eb1ec0979d4f43cc3c837e4898839b7c5fd6c154 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sat, 18 Mar 2017 10:59:30 -0400 Subject: [PATCH 166/269] separated root bootstrap and responsive files into different WRO4J groups --- openid-connect-server-webapp/pom.xml | 2 +- openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.xml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index b3eaa31b60..96fbcc7b7c 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -96,7 +96,7 @@ - bootstrap + bootstrap,bootstrap-responsive ${project.build.directory}/${project.build.finalName} ${project.build.directory}/${project.build.finalName}/resources/bootstrap2/css/ ${project.build.directory}/${project.build.finalName}/js/ diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.xml index a69a55bb9f..fdd66a0b40 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.xml @@ -1,6 +1,8 @@ /less/bootstrap.less + + /less/bootstrap-responsive.less - + \ No newline at end of file From 4da1e619a075388922fb9be98e28b1039731fd45 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Sat, 18 Mar 2017 11:24:01 -0400 Subject: [PATCH 167/269] plugin management, removed less files from .war --- openid-connect-server-webapp/pom.xml | 11 +---------- pom.xml | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index 96fbcc7b7c..df16b8c83b 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -58,6 +58,7 @@ + less/** @@ -86,15 +87,6 @@ ro.isdc.wro4j wro4j-maven-plugin - 1.8.0 - - - compile - - run - - - bootstrap,bootstrap-responsive ${project.build.directory}/${project.build.finalName} @@ -110,7 +102,6 @@ ro.isdc.wro4j wro4j-extensions - 1.8.0 org.mitre diff --git a/pom.xml b/pom.xml index 9b9d7e443e..65d3e18eb0 100644 --- a/pom.xml +++ b/pom.xml @@ -150,6 +150,19 @@ + + ro.isdc.wro4j + wro4j-maven-plugin + 1.8.0 + + + compile + + run + + + + @@ -568,6 +581,11 @@ commons-io 1.3.2 + + ro.isdc.wro4j + wro4j-extensions + 1.8.0 + From 80358566a55a1335137b146bf1a4c149bb107017 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 20 Mar 2017 11:58:52 -0400 Subject: [PATCH 168/269] [maven-release-plugin] prepare release mitreid-connect-1.3.0-RC2 --- openid-connect-client/pom.xml | 2 +- openid-connect-common/pom.xml | 2 +- openid-connect-server-webapp/pom.xml | 2 +- openid-connect-server/pom.xml | 2 +- pom.xml | 2 +- uma-server-webapp/pom.xml | 2 +- uma-server/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml index a42644801c..eb5b36b3f4 100644 --- a/openid-connect-client/pom.xml +++ b/openid-connect-client/pom.xml @@ -21,7 +21,7 @@ openid-connect-parent org.mitre - 1.3.0-RC2-SNAPSHOT + 1.3.0-RC2 .. openid-connect-client diff --git a/openid-connect-common/pom.xml b/openid-connect-common/pom.xml index 50cb25e7f7..966ae2d689 100644 --- a/openid-connect-common/pom.xml +++ b/openid-connect-common/pom.xml @@ -21,7 +21,7 @@ openid-connect-parent org.mitre - 1.3.0-RC2-SNAPSHOT + 1.3.0-RC2 .. openid-connect-common diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index df16b8c83b..f95fcd6b6b 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.3.0-RC2-SNAPSHOT + 1.3.0-RC2 openid-connect-server-webapp war diff --git a/openid-connect-server/pom.xml b/openid-connect-server/pom.xml index c7e47dfdf7..dd58b79fe8 100644 --- a/openid-connect-server/pom.xml +++ b/openid-connect-server/pom.xml @@ -22,7 +22,7 @@ org.mitre openid-connect-parent - 1.3.0-RC2-SNAPSHOT + 1.3.0-RC2 .. diff --git a/pom.xml b/pom.xml index 65d3e18eb0..c0a1edb340 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 4.0.0 org.mitre openid-connect-parent - 1.3.0-RC2-SNAPSHOT + 1.3.0-RC2 MITREid Connect pom diff --git a/uma-server-webapp/pom.xml b/uma-server-webapp/pom.xml index 00cec856eb..ea7cc3d7b6 100644 --- a/uma-server-webapp/pom.xml +++ b/uma-server-webapp/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.3.0-RC2-SNAPSHOT + 1.3.0-RC2 .. uma-server-webapp diff --git a/uma-server/pom.xml b/uma-server/pom.xml index 6ac6925e81..623ad7a489 100644 --- a/uma-server/pom.xml +++ b/uma-server/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.3.0-RC2-SNAPSHOT + 1.3.0-RC2 .. uma-server From dd0f69ba6d0309500d47520025a5b0a6e73d6057 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 20 Mar 2017 11:58:58 -0400 Subject: [PATCH 169/269] [maven-release-plugin] prepare for next development iteration --- openid-connect-client/pom.xml | 2 +- openid-connect-common/pom.xml | 2 +- openid-connect-server-webapp/pom.xml | 2 +- openid-connect-server/pom.xml | 2 +- pom.xml | 2 +- uma-server-webapp/pom.xml | 2 +- uma-server/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml index eb5b36b3f4..d22283da7e 100644 --- a/openid-connect-client/pom.xml +++ b/openid-connect-client/pom.xml @@ -21,7 +21,7 @@ openid-connect-parent org.mitre - 1.3.0-RC2 + 1.3.0-RC3-SNAPSHOT .. openid-connect-client diff --git a/openid-connect-common/pom.xml b/openid-connect-common/pom.xml index 966ae2d689..5edafd5c34 100644 --- a/openid-connect-common/pom.xml +++ b/openid-connect-common/pom.xml @@ -21,7 +21,7 @@ openid-connect-parent org.mitre - 1.3.0-RC2 + 1.3.0-RC3-SNAPSHOT .. openid-connect-common diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index f95fcd6b6b..528201c049 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.3.0-RC2 + 1.3.0-RC3-SNAPSHOT openid-connect-server-webapp war diff --git a/openid-connect-server/pom.xml b/openid-connect-server/pom.xml index dd58b79fe8..96ad23d087 100644 --- a/openid-connect-server/pom.xml +++ b/openid-connect-server/pom.xml @@ -22,7 +22,7 @@ org.mitre openid-connect-parent - 1.3.0-RC2 + 1.3.0-RC3-SNAPSHOT .. diff --git a/pom.xml b/pom.xml index c0a1edb340..a8bbcf5e82 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 4.0.0 org.mitre openid-connect-parent - 1.3.0-RC2 + 1.3.0-RC3-SNAPSHOT MITREid Connect pom diff --git a/uma-server-webapp/pom.xml b/uma-server-webapp/pom.xml index ea7cc3d7b6..81f4705057 100644 --- a/uma-server-webapp/pom.xml +++ b/uma-server-webapp/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.3.0-RC2 + 1.3.0-RC3-SNAPSHOT .. uma-server-webapp diff --git a/uma-server/pom.xml b/uma-server/pom.xml index 623ad7a489..5bfde7812e 100644 --- a/uma-server/pom.xml +++ b/uma-server/pom.xml @@ -20,7 +20,7 @@ org.mitre openid-connect-parent - 1.3.0-RC2 + 1.3.0-RC3-SNAPSHOT .. uma-server From b6cf8fe22be7cbf952341fa3a2286ab16b3fad3a Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 20 Mar 2017 13:45:55 -0400 Subject: [PATCH 170/269] cleanup --- .gitignore | 3 --- LICENSE.txt | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index a403cd3d80..e663c16b8f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -local-values.conf target *~ bin @@ -11,5 +10,3 @@ bin /target .springBeans nb-configuration.xml -openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap-responsive.css -openid-connect-server-webapp/src/main/webapp/resources/bootstrap2/css/bootstrap.css diff --git a/LICENSE.txt b/LICENSE.txt index 093874f21f..12d29da560 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright 2016 The MITRE Corporation +Copyright 2017 The MITRE Corporation and the MIT Internet Trust Consortium Licensed under the Apache License, Version 2.0 (the "License"); From 32ce21b5cd7fa9cbdefa89bd723d8ef5ba8d74ae Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 21 Mar 2017 13:36:20 -0400 Subject: [PATCH 171/269] automated code formatting and cleanup --- .../IntrospectingTokenService.java | 4 +- .../IntrospectionAuthorityGranter.java | 2 +- .../IntrospectionConfigurationService.java | 2 +- ...singIntrospectionConfigurationService.java | 6 +- ...eBasedIntrospectionAuthoritiesGranter.java | 6 +- .../SimpleIntrospectionAuthorityGranter.java | 6 +- ...aticIntrospectionConfigurationService.java | 6 +- .../AuthorizationEndpointException.java | 4 +- .../client/NamedAdminAuthoritiesMapper.java | 8 +- .../client/OIDCAuthenticationFilter.java | 32 +- .../client/OIDCAuthenticationProvider.java | 8 +- .../StaticPrefixTargetLinkURIChecker.java | 2 +- .../client/SubjectIssuerGrantedAuthority.java | 10 +- .../connect/client/TargetLinkURIChecker.java | 2 +- .../connect/client/UserInfoFetcher.java | 21 +- .../ClientKeyPublisherMapping.java | 2 +- .../client/keypublisher/JwkViewResolver.java | 6 +- .../client/model/IssuerServiceResponse.java | 6 +- .../service/AuthRequestOptionsService.java | 10 +- .../client/service/AuthRequestUrlBuilder.java | 4 +- .../service/ClientConfigurationService.java | 2 +- .../connect/client/service/IssuerService.java | 6 +- .../service/RegisteredClientService.java | 4 +- .../service/ServerConfigurationService.java | 2 +- ...egistrationClientConfigurationService.java | 16 +- .../DynamicServerConfigurationService.java | 26 +- .../impl/EncryptedAuthRequestUrlBuilder.java | 2 +- .../HybridClientConfigurationService.java | 8 +- .../service/impl/HybridIssuerService.java | 4 +- .../HybridServerConfigurationService.java | 8 +- .../impl/InMemoryRegisteredClientService.java | 2 +- .../impl/JsonFileRegisteredClientService.java | 30 +- .../impl/PlainAuthRequestUrlBuilder.java | 6 +- .../impl/SignedAuthRequestUrlBuilder.java | 2 +- .../impl/StaticAuthRequestOptionsService.java | 6 +- .../StaticClientConfigurationService.java | 8 +- .../StaticServerConfigurationService.java | 4 +- .../impl/StaticSingleIssuerService.java | 4 +- .../service/impl/ThirdPartyIssuerService.java | 6 +- .../service/impl/WebfingerIssuerService.java | 10 +- ...eBasedIntrospectionAuthoritiesGranter.java | 14 +- .../client/TestOIDCAuthenticationFilter.java | 22 +- .../impl/TestSignedAuthRequestUrlBuilder.java | 2 +- .../data/AbstractPageOperationTemplate.java | 318 +++---- .../org/mitre/data/DefaultPageCriteria.java | 38 +- .../java/org/mitre/data/PageCriteria.java | 4 +- .../util/WebfingerURLNormalizer.java | 2 +- .../mitre/jose/keystore/JWKSetKeyStore.java | 2 +- .../jwt/assertion/AssertionValidator.java | 2 +- .../impl/NullAssertionValidator.java | 2 +- .../impl/SelfAssertionValidator.java | 14 +- .../WhitelistedIssuerAssertionValidator.java | 20 +- ...aultJWTEncryptionAndDecryptionService.java | 11 +- .../JWTSigningAndValidationService.java | 6 +- .../service/impl/ClientKeyCacheService.java | 4 +- ...DefaultJWTSigningAndValidationService.java | 28 +- .../service/impl/JWKSetCacheService.java | 14 +- .../SymmetricKeyJWTValidatorCacheService.java | 4 +- .../oauth2/model/AuthorizationCodeEntity.java | 4 +- .../oauth2/model/ClientDetailsEntity.java | 21 +- .../org/mitre/oauth2/model/DeviceCode.java | 14 +- .../oauth2/model/OAuth2AccessTokenEntity.java | 9 +- .../model/OAuth2RefreshTokenEntity.java | 6 +- .../org/mitre/oauth2/model/PKCEAlgorithm.java | 15 +- .../mitre/oauth2/model/RegisteredClient.java | 6 +- .../oauth2/model/SavedUserAuthentication.java | 2 +- .../org/mitre/oauth2/model/SystemScope.java | 3 +- .../convert/SerializableStringConverter.java | 4 +- .../AuthenticationHolderRepository.java | 2 +- .../AuthorizationCodeRepository.java | 18 +- .../repository/OAuth2TokenRepository.java | 46 +- .../repository/SystemScopeRepository.java | 2 +- .../repository/impl/DeviceCodeRepository.java | 1 - .../oauth2/service/DeviceCodeService.java | 3 +- .../oauth2/service/SystemScopeService.java | 16 +- .../impl/DefaultClientUserDetailsService.java | 6 +- .../UriEncodedClientUserDetailsService.java | 6 +- .../ClientDetailsEntityJsonProcessor.java | 36 +- .../ConfigurationBeanLocaleResolver.java | 7 +- .../config/ConfigurationPropertiesBean.java | 84 +- .../openid/connect/config/JWKSetEditor.java | 2 +- .../connect/config/ServerConfiguration.java | 8 +- .../connect/config/UIConfiguration.java | 8 +- .../mitre/openid/connect/model/Address.java | 24 +- .../openid/connect/model/ApprovedSite.java | 6 - .../openid/connect/model/BlacklistedSite.java | 2 +- .../openid/connect/model/CachedImage.java | 4 +- .../openid/connect/model/ClientStat.java | 2 +- .../openid/connect/model/DefaultAddress.java | 15 +- .../openid/connect/model/DefaultUserInfo.java | 4 +- .../model/OIDCAuthenticationToken.java | 10 +- .../connect/model/PairwiseIdentifier.java | 6 +- .../model/PendingOIDCAuthenticationToken.java | 8 +- .../mitre/openid/connect/model/UserInfo.java | 10 +- .../connect/repository/AddressRepository.java | 2 +- .../repository/ApprovedSiteRepository.java | 10 +- .../repository/BlacklistedSiteRepository.java | 2 +- .../PairwiseIdentifierRepository.java | 6 +- .../repository/UserInfoRepository.java | 6 +- .../repository/WhitelistedSiteRepository.java | 16 +- .../connect/service/ApprovedSiteService.java | 16 +- .../service/BlacklistedSiteService.java | 2 +- .../connect/service/MITREidDataService.java | 8 +- .../service/MITREidDataServiceExtension.java | 16 +- .../service/MITREidDataServiceMaps.java | 2 +- .../connect/service/OIDCTokenService.java | 8 +- .../service/PairwiseIdentiferService.java | 6 +- .../service/ScopeClaimTranslationService.java | 2 +- .../openid/connect/service/StatsService.java | 6 +- .../connect/service/UserInfoService.java | 6 +- .../service/WhitelistedSiteService.java | 14 +- .../mitre/openid/connect/view/JWKSetView.java | 2 +- .../connect/web/UserInfoInterceptor.java | 26 +- .../main/java/org/mitre/uma/model/Claim.java | 12 +- .../uma/model/ClaimProcessingResult.java | 2 +- .../java/org/mitre/uma/model/Permission.java | 6 +- .../org/mitre/uma/model/PermissionTicket.java | 6 +- .../main/java/org/mitre/uma/model/Policy.java | 8 +- .../java/org/mitre/uma/model/ResourceSet.java | 6 +- .../uma/model/SavedRegisteredClient.java | 2 +- .../uma/repository/PermissionRepository.java | 16 +- .../uma/service/ClaimsProcessingService.java | 8 +- .../mitre/uma/service/PermissionService.java | 6 +- .../mitre/uma/service/ResourceSetService.java | 4 +- .../service/SavedRegisteredClientService.java | 4 +- .../mitre/uma/service/UmaTokenService.java | 4 +- .../main/java/org/mitre/util/JsonUtils.java | 62 +- .../main/java/org/mitre/util/jpa/JpaUtil.java | 50 +- .../AbstractPageOperationTemplateTest.java | 404 ++++---- .../org/mitre/jose/TestJWKSetKeyStore.java | 40 +- ...aultJWTEncryptionAndDecryptionService.java | 66 +- .../oauth2/model/ClientDetailsEntityTest.java | 2 +- .../oauth2/model/RegisteredClientTest.java | 2 +- .../ClientDetailsEntityJsonProcessorTest.java | 2 +- .../ConfigurationPropertiesBeanTest.java | 4 +- .../config/ServerConfigurationTest.java | 2 +- .../mitre/discovery/view/WebfingerView.java | 44 +- .../discovery/web/DiscoveryEndpoint.java | 26 +- .../AssertionOAuth2RequestFactory.java | 2 +- .../impl/DirectCopyRequestFactory.java | 11 +- .../AuthorizationPendingException.java | 6 +- .../exception/DeviceCodeExpiredException.java | 2 +- .../exception/DuplicateClientIdException.java | 2 +- .../JpaAuthenticationHolderRepository.java | 16 +- .../impl/JpaAuthorizationCodeRepository.java | 16 +- .../impl/JpaDeviceCodeRepository.java | 4 +- .../impl/JpaOAuth2TokenRepository.java | 36 +- .../impl/JpaSystemScopeRepository.java | 8 +- .../impl/BlacklistAwareRedirectResolver.java | 10 +- .../impl/DefaultDeviceCodeService.java | 49 +- .../DefaultIntrospectionResultAssembler.java | 4 +- ...DefaultOAuth2AuthorizationCodeService.java | 30 +- ...faultOAuth2ClientDetailsEntityService.java | 42 +- .../DefaultOAuth2ProviderTokenService.java | 92 +- .../impl/DefaultSystemScopeService.java | 6 +- .../oauth2/token/ChainedTokenGranter.java | 2 +- .../oauth2/token/DeviceTokenGranter.java | 30 +- .../token/JWTAssertionTokenGranter.java | 26 +- ...opeServiceAwareOAuth2RequestValidator.java | 8 +- .../org/mitre/oauth2/view/TokenApiView.java | 92 +- .../oauth2/web/AuthenticationUtilities.java | 4 +- .../java/org/mitre/oauth2/web/CorsFilter.java | 6 +- .../org/mitre/oauth2/web/DeviceEndpoint.java | 76 +- .../oauth2/web/IntrospectionEndpoint.java | 6 +- .../oauth2/web/OAuth2ExceptionHandler.java | 2 +- .../web/OAuthConfirmationController.java | 8 +- .../mitre/oauth2/web/RevocationEndpoint.java | 22 +- .../java/org/mitre/oauth2/web/ScopeAPI.java | 2 +- ...JWTBearerAssertionAuthenticationToken.java | 4 +- .../JWTBearerAuthenticationProvider.java | 14 +- ...rerClientAssertionTokenEndpointFilter.java | 4 +- .../connect/config/JsonMessageSource.java | 22 +- .../filter/AuthorizationRequestFilter.java | 16 +- .../filter/MultiUrlRequestMatcher.java | 6 +- .../repository/impl/JpaAddressRepository.java | 4 +- .../impl/JpaApprovedSiteRepository.java | 6 +- .../impl/JpaBlacklistedSiteRepository.java | 6 +- .../impl/JpaPairwiseIdentifierRepository.java | 8 +- .../impl/JpaUserInfoRepository.java | 6 +- .../impl/JpaWhitelistedSiteRepository.java | 6 +- .../request/ConnectOAuth2RequestFactory.java | 12 +- .../request/ConnectRequestParameters.java | 6 +- .../impl/DefaultApprovedSiteService.java | 6 +- .../impl/DefaultBlacklistedSiteService.java | 2 +- .../service/impl/DefaultOIDCTokenService.java | 14 +- .../DefaultScopeClaimTranslationService.java | 4 +- .../service/impl/DefaultStatsService.java | 10 +- .../service/impl/DefaultUserInfoService.java | 4 +- .../impl/DefaultWhitelistedSiteService.java | 4 +- .../service/impl/DummyResourceSetService.java | 2 +- .../InMemoryClientLogoLoadingService.java | 20 +- .../service/impl/MITREidDataService_1_0.java | 860 ++++++++--------- .../service/impl/MITREidDataService_1_1.java | 878 +++++++++--------- .../service/impl/MITREidDataService_1_2.java | 793 ++++++++-------- .../service/impl/MITREidDataService_1_3.java | 832 ++++++++--------- .../impl/MatchLoginHintsAgainstUsers.java | 2 +- .../service/impl/PassAllLoginHints.java | 2 +- .../impl/RemoveLoginHintsWithHTTP.java | 2 +- .../impl/UUIDPairwiseIdentiferService.java | 2 +- .../connect/token/ConnectTokenEnhancer.java | 21 +- .../token/TofuUserApprovalHandler.java | 21 +- .../openid/connect/util/IdTokenHashUtils.java | 8 +- .../view/AbstractClientEntityView.java | 141 ++- .../view/ClientEntityViewForAdmins.java | 6 +- .../view/ClientEntityViewForUsers.java | 6 +- .../view/ClientInformationResponseView.java | 6 +- .../openid/connect/view/HttpCodeView.java | 2 +- .../connect/view/JsonApprovedSiteView.java | 70 +- .../openid/connect/view/JsonEntityView.java | 44 +- .../openid/connect/view/JsonErrorView.java | 46 +- .../openid/connect/view/UserInfoJWTView.java | 12 +- .../openid/connect/view/UserInfoView.java | 8 +- .../openid/connect/web/ApprovedSiteAPI.java | 8 +- .../web/AuthenticationTimeStamper.java | 4 +- .../openid/connect/web/BlacklistAPI.java | 4 +- .../mitre/openid/connect/web/ClientAPI.java | 215 ++--- .../org/mitre/openid/connect/web/DataAPI.java | 61 +- .../DynamicClientRegistrationEndpoint.java | 277 +++--- ...ProtectedResourceRegistrationEndpoint.java | 10 +- .../connect/web/ServerConfigInterceptor.java | 8 +- .../mitre/openid/connect/web/StatsAPI.java | 20 +- .../openid/connect/web/UserInfoEndpoint.java | 2 +- .../openid/connect/web/WhitelistAPI.java | 4 +- .../TestBlacklistAwareRedirectResolver.java | 8 +- ...stDefaultIntrospectionResultAssembler.java | 103 +- ...faultOAuth2ClientDetailsEntityService.java | 101 +- .../impl/TestDefaultSystemScopeService.java | 2 - .../impl/TestDefaultApprovedSiteService.java | 7 +- .../service/impl/TestDefaultStatsService.java | 1 - .../impl/TestDefaultUserInfoService.java | 2 +- .../impl/TestMITREidDataService_1_0.java | 10 +- .../impl/TestMITREidDataService_1_1.java | 10 +- .../impl/TestMITREidDataService_1_2.java | 21 +- .../impl/TestMITREidDataService_1_3.java | 13 +- .../TestUUIDPairwiseIdentiferService.java | 2 +- .../connect/util/TestIdTokenHashUtils.java | 2 +- .../impl/MatchAllClaimsOnAnyPolicy.java | 2 +- .../impl/UmaDataServiceExtension_1_3.java | 508 +++++----- .../util/ExternalLoginAuthoritiesMapper.java | 2 +- .../ResourceSetEntityAbbreviatedView.java | 44 +- .../mitre/uma/view/ResourceSetEntityView.java | 44 +- .../uma/web/AuthorizationRequestEndpoint.java | 4 - .../uma/web/ClaimsCollectionEndpoint.java | 4 +- .../web/PermissionRegistrationEndpoint.java | 8 +- .../java/org/mitre/uma/web/PolicyAPI.java | 2 +- .../web/ResourceSetRegistrationEndpoint.java | 14 +- .../impl/TestDefaultPermissionService.java | 4 +- .../impl/TestDefaultResourceSetService.java | 4 +- 248 files changed, 4079 insertions(+), 4198 deletions(-) diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java index 62f65c7fde..595cc4a49c 100644 --- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java +++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java @@ -16,6 +16,8 @@ *******************************************************************************/ package org.mitre.oauth2.introspectingfilter; +import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.SECRET_BASIC; + import java.io.IOException; import java.net.URI; import java.util.Calendar; @@ -54,8 +56,6 @@ import com.google.gson.JsonParser; import com.nimbusds.jose.util.Base64; -import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.SECRET_BASIC; - /** * This ResourceServerTokenServices implementation introspects incoming tokens at a * server's introspection endpoint URL and passes an Authentication object along diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionAuthorityGranter.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionAuthorityGranter.java index 0117f5aca9..6d3526b5a8 100644 --- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionAuthorityGranter.java +++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionAuthorityGranter.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.introspectingfilter.service; diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionConfigurationService.java index 6223779f25..192d2e7a6b 100644 --- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionConfigurationService.java +++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionConfigurationService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.introspectingfilter.service; diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/JWTParsingIntrospectionConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/JWTParsingIntrospectionConfigurationService.java index 90ff0278c3..e1f04aa0e5 100644 --- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/JWTParsingIntrospectionConfigurationService.java +++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/JWTParsingIntrospectionConfigurationService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.introspectingfilter.service.impl; @@ -32,11 +32,11 @@ import com.nimbusds.jwt.JWTParser; /** - * + * * Parses the incoming accesstoken as a JWT and determines the issuer based on * the "iss" field inside the JWT. Uses the ServerConfigurationService to determine * the introspection URL for that issuer. - * + * * @author jricher * */ diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/ScopeBasedIntrospectionAuthoritiesGranter.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/ScopeBasedIntrospectionAuthoritiesGranter.java index 346041c332..f6fa4e9a45 100644 --- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/ScopeBasedIntrospectionAuthoritiesGranter.java +++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/ScopeBasedIntrospectionAuthoritiesGranter.java @@ -34,7 +34,7 @@ * */ public class ScopeBasedIntrospectionAuthoritiesGranter implements IntrospectionAuthorityGranter { - + private List authorities = AuthorityUtils.createAuthorityList("ROLE_API"); /* (non-Javadoc) @@ -43,7 +43,7 @@ public class ScopeBasedIntrospectionAuthoritiesGranter implements IntrospectionA @Override public List getAuthorities(JsonObject introspectionResponse) { List auth = new ArrayList<>(getAuthorities()); - + if (introspectionResponse.has("scope") && introspectionResponse.get("scope").isJsonPrimitive()) { String scopeString = introspectionResponse.get("scope").getAsString(); Set scopes = OAuth2Utils.parseParameterList(scopeString); @@ -51,7 +51,7 @@ public List getAuthorities(JsonObject introspectionResponse) { auth.add(new SimpleGrantedAuthority("OAUTH_SCOPE_" + scope)); } } - + return auth; } diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/SimpleIntrospectionAuthorityGranter.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/SimpleIntrospectionAuthorityGranter.java index 1bb4363ea8..3b966aadc0 100644 --- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/SimpleIntrospectionAuthorityGranter.java +++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/SimpleIntrospectionAuthorityGranter.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.introspectingfilter.service.impl; @@ -28,9 +28,9 @@ import com.google.gson.JsonObject; /** - * + * * Grants the same set of authorities no matter what's passed in. - * + * * @author jricher * */ diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/StaticIntrospectionConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/StaticIntrospectionConfigurationService.java index 18c8f7003e..85d2ec3f79 100644 --- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/StaticIntrospectionConfigurationService.java +++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/StaticIntrospectionConfigurationService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.introspectingfilter.service.impl; @@ -23,10 +23,10 @@ import org.mitre.oauth2.model.RegisteredClient; /** - * + * * Always provides the (configured) IntrospectionURL and RegisteredClient regardless * of token. Useful for talking to a single, trusted authorization server. - * + * * @author jricher * */ diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java index be017f4739..23b5773f5d 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java @@ -7,9 +7,9 @@ public class AuthorizationEndpointException extends AuthenticationServiceExcepti private static final long serialVersionUID = 6953119789654778380L; private String error; - + private String errorDescription; - + private String errorURI; public AuthorizationEndpointException(String error, String errorDescription, String errorURI) { diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/NamedAdminAuthoritiesMapper.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/NamedAdminAuthoritiesMapper.java index 99793dfac3..b13b32200f 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/NamedAdminAuthoritiesMapper.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/NamedAdminAuthoritiesMapper.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.client; @@ -34,13 +34,13 @@ import com.nimbusds.jwt.JWTClaimsSet; /** - * + * * Simple mapper that adds ROLE_USER to the authorities map for all queries, * plus adds ROLE_ADMIN if the subject and issuer pair are found in the * configurable "admins" set. - * + * * @author jricher - * + * */ public class NamedAdminAuthoritiesMapper implements OIDCAuthoritiesMapper { diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java index 6a52b3f3c3..62b5a7027c 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java @@ -88,9 +88,9 @@ /** * OpenID Connect Authentication Filter class - * + * * @author nemonik, jricher - * + * */ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFilter { @@ -169,9 +169,9 @@ public void afterPropertiesSet() { /* * This is the main entry point for the filter. - * + * * (non-Javadoc) - * + * * @see org.springframework.security.web.authentication. * AbstractAuthenticationProcessingFilter * #attemptAuthentication(javax.servlet.http.HttpServletRequest, @@ -204,7 +204,7 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ /** * Initiate an Authorization request - * + * * @param request * The request from which to extract parameters and perform the * authentication @@ -270,7 +270,7 @@ protected void handleAuthorizationRequest(HttpServletRequest request, HttpServle String state = createState(session); Map options = authOptions.getOptions(serverConfig, clientConfig, request); - + // if we're using PKCE, handle the challenge here if (clientConfig.getCodeChallengeMethod() != null) { String codeVerifier = createCodeVerifier(session); @@ -287,7 +287,7 @@ protected void handleAuthorizationRequest(HttpServletRequest request, HttpServle e.printStackTrace(); } - + } } @@ -330,7 +330,7 @@ protected Authentication handleAuthorizationCodeResponse(HttpServletRequest requ form.add("grant_type", "authorization_code"); form.add("code", authorizationCode); form.setAll(authOptions.getTokenOptions(serverConfig, clientConfig, request)); - + String codeVerifier = getStoredCodeVerifier(session); if (codeVerifier != null) { form.add("code_verifier", codeVerifier); @@ -345,11 +345,11 @@ protected Authentication handleAuthorizationCodeResponse(HttpServletRequest requ if(httpClient == null) { httpClient = HttpClientBuilder.create() - .useSystemProperties() - .setDefaultRequestConfig(RequestConfig.custom() - .setSocketTimeout(httpSocketTimeout) - .build()) - .build(); + .useSystemProperties() + .setDefaultRequestConfig(RequestConfig.custom() + .setSocketTimeout(httpSocketTimeout) + .build()) + .build(); } HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); @@ -634,7 +634,7 @@ protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOE /** * Handle Authorization Endpoint error - * + * * @param request * The request from which to extract parameters and handle the * error @@ -708,7 +708,7 @@ protected static String createState(HttpSession session) { protected static String getStoredState(HttpSession session) { return getStoredSessionString(session, STATE_SESSION_VARIABLE); } - + /** * Create a random code challenge and store it in the session * @param session @@ -719,7 +719,7 @@ protected static String createCodeVerifier(HttpSession session) { session.setAttribute(CODE_VERIFIER_SESSION_VARIABLE, challenge); return challenge; } - + /** * Retrieve the stored challenge from our session * @param session diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationProvider.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationProvider.java index de7e7fdf0e..462c194a17 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationProvider.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationProvider.java @@ -34,7 +34,7 @@ /** * @author nemonik, Justin Richer - * + * */ public class OIDCAuthenticationProvider implements AuthenticationProvider { @@ -46,7 +46,7 @@ public class OIDCAuthenticationProvider implements AuthenticationProvider { /* * (non-Javadoc) - * + * * @see org.springframework.security.authentication.AuthenticationProvider# * authenticate(org.springframework.security.core.Authentication) */ @@ -86,7 +86,7 @@ public Authentication authenticate(final Authentication authentication) throws A /** * Override this function to return a different kind of Authentication, processes the authorities differently, * or do post-processing based on the UserInfo object. - * + * * @param token * @param authorities * @param userInfo @@ -115,7 +115,7 @@ public void setAuthoritiesMapper(OIDCAuthoritiesMapper authoritiesMapper) { /* * (non-Javadoc) - * + * * @see * org.springframework.security.authentication.AuthenticationProvider#supports * (java.lang.Class) diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/StaticPrefixTargetLinkURIChecker.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/StaticPrefixTargetLinkURIChecker.java index ebf640ba59..906741d70b 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/StaticPrefixTargetLinkURIChecker.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/StaticPrefixTargetLinkURIChecker.java @@ -19,7 +19,7 @@ /** * Simple target URI checker, checks whether the string in question starts * with a configured prefix. Returns "/" if the match fails. - * + * * @author jricher * */ diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/SubjectIssuerGrantedAuthority.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/SubjectIssuerGrantedAuthority.java index e6620a0705..45b6e7e317 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/SubjectIssuerGrantedAuthority.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/SubjectIssuerGrantedAuthority.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.client; @@ -24,9 +24,9 @@ import com.google.common.base.Strings; /** - * + * * Simple authority representing a user at an issuer. - * + * * @author jricher * */ @@ -51,9 +51,9 @@ public SubjectIssuerGrantedAuthority(String subject, String issuer) { /** * Returns a string formed by concatenating the subject with the issuer, separated by _ and prepended with OIDC_ - * + * * For example, the user "bob" from issuer "http://id.example.com/" would return the authority string of: - * + * * OIDC_bob_http://id.example.com/ */ @Override diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/TargetLinkURIChecker.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/TargetLinkURIChecker.java index ba3112a072..9d688334c1 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/TargetLinkURIChecker.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/TargetLinkURIChecker.java @@ -20,7 +20,7 @@ public interface TargetLinkURIChecker { /** * Check the parameter to make sure that it's a valid deep-link into this application. - * + * * @param target * @return */ diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java index ecaed19e88..faa9747784 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java @@ -60,7 +60,7 @@ public class UserInfoFetcher { private static final Logger logger = LoggerFactory.getLogger(UserInfoFetcher.class); private LoadingCache cache; - + public UserInfoFetcher() { this(HttpClientBuilder.create().useSystemProperties().build()); } @@ -71,7 +71,7 @@ public UserInfoFetcher(HttpClient httpClient) { .maximumSize(100) .build(new UserInfoLoader(httpClient)); } - + public UserInfo loadUserInfo(final PendingOIDCAuthenticationToken token) { try { return cache.get(token); @@ -81,8 +81,8 @@ public UserInfo loadUserInfo(final PendingOIDCAuthenticationToken token) { } } - - + + private class UserInfoLoader extends CacheLoader { private HttpComponentsClientHttpRequestFactory factory; @@ -90,22 +90,23 @@ private class UserInfoLoader extends CacheLoader blacklist) { /** * Loader class that fetches the client information. - * + * * If a client has been registered (ie, it's known to the RegisteredClientService), then this * will fetch the client's configuration from the server. - * + * * @author jricher * */ @@ -203,12 +203,12 @@ public RegisteredClient load(ServerConfiguration serverConfig) throws Exception try { String registered = restTemplate.postForObject(serverConfig.getRegistrationEndpointUri(), entity, String.class); - + RegisteredClient client = ClientDetailsEntityJsonProcessor.parseRegistered(registered); - + // save this client for later registeredClientService.save(serverConfig.getIssuer(), client); - + return client; } catch (RestClientException rce) { throw new InvalidClientException("Error registering client with server"); @@ -227,9 +227,9 @@ public RegisteredClient load(ServerConfiguration serverConfig) throws Exception try { String registered = restTemplate.exchange(knownClient.getRegistrationClientUri(), HttpMethod.GET, entity, String.class).getBody(); // TODO: handle HTTP errors - + RegisteredClient client = ClientDetailsEntityJsonProcessor.parseRegistered(registered); - + return client; } catch (RestClientException rce) { throw new InvalidClientException("Error loading previously registered client information from server"); diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java index 02646f7ba4..5d80cce51b 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java @@ -15,10 +15,17 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.client.service.impl; +import static org.mitre.util.JsonUtils.getAsBoolean; +import static org.mitre.util.JsonUtils.getAsEncryptionMethodList; +import static org.mitre.util.JsonUtils.getAsJweAlgorithmList; +import static org.mitre.util.JsonUtils.getAsJwsAlgorithmList; +import static org.mitre.util.JsonUtils.getAsString; +import static org.mitre.util.JsonUtils.getAsStringList; + import java.util.HashSet; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -41,17 +48,10 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import static org.mitre.util.JsonUtils.getAsBoolean; -import static org.mitre.util.JsonUtils.getAsEncryptionMethodList; -import static org.mitre.util.JsonUtils.getAsJweAlgorithmList; -import static org.mitre.util.JsonUtils.getAsJwsAlgorithmList; -import static org.mitre.util.JsonUtils.getAsString; -import static org.mitre.util.JsonUtils.getAsStringList; - /** - * + * * Dynamically fetches OpenID Connect server configurations based on the issuer. Caches the server configurations. - * + * * @author jricher * */ @@ -133,9 +133,9 @@ private class OpenIDConnectServiceConfigurationFetcher extends CacheLoader() { - @Override - public JsonElement serialize(RegisteredClient src, Type typeOfSrc, JsonSerializationContext context) { - return ClientDetailsEntityJsonProcessor.serialize(src); - } - }) - .registerTypeAdapter(RegisteredClient.class, new JsonDeserializer() { - @Override - public RegisteredClient deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - return ClientDetailsEntityJsonProcessor.parseRegistered(json); - } - }) - .setPrettyPrinting() - .create(); + .registerTypeAdapter(RegisteredClient.class, new JsonSerializer() { + @Override + public JsonElement serialize(RegisteredClient src, Type typeOfSrc, JsonSerializationContext context) { + return ClientDetailsEntityJsonProcessor.serialize(src); + } + }) + .registerTypeAdapter(RegisteredClient.class, new JsonDeserializer() { + @Override + public RegisteredClient deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return ClientDetailsEntityJsonProcessor.parseRegistered(json); + } + }) + .setPrettyPrinting() + .create(); private File file; diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/PlainAuthRequestUrlBuilder.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/PlainAuthRequestUrlBuilder.java index 90d3ad9635..d7d4916024 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/PlainAuthRequestUrlBuilder.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/PlainAuthRequestUrlBuilder.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.client.service.impl; @@ -33,9 +33,9 @@ import com.google.common.base.Strings; /** - * + * * Builds an auth request redirect URI with normal query parameters. - * + * * @author jricher * */ diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/SignedAuthRequestUrlBuilder.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/SignedAuthRequestUrlBuilder.java index 5051f2cd62..1a8c855ea4 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/SignedAuthRequestUrlBuilder.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/SignedAuthRequestUrlBuilder.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.client.service.impl; diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticAuthRequestOptionsService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticAuthRequestOptionsService.java index 7b18c6a0ae..6b91b4d60f 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticAuthRequestOptionsService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticAuthRequestOptionsService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.client.service.impl; @@ -29,9 +29,9 @@ import org.mitre.openid.connect.config.ServerConfiguration; /** - * + * * Always returns the same set of options. - * + * * @author jricher * */ diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticClientConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticClientConfigurationService.java index 89be2391ab..f11bb3ccde 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticClientConfigurationService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticClientConfigurationService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.client.service.impl; @@ -29,9 +29,9 @@ /** * Client configuration service that holds a static map from issuer URL to a ClientDetails object to use at that issuer. - * + * * Designed to be configured as a bean. - * + * * @author jricher * */ @@ -56,7 +56,7 @@ public void setClients(Map clients) { /** * Get the client configured for this issuer - * + * * @see org.mitre.openid.connect.client.service.ClientConfigurationService#getClientConfiguration(java.lang.String) */ @Override diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticServerConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticServerConfigurationService.java index a3432cbbae..43e3964308 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticServerConfigurationService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticServerConfigurationService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.client.service.impl; @@ -28,7 +28,7 @@ /** * Statically configured server configuration service that maps issuer URLs to server configurations to use at that issuer. - * + * * @author jricher * */ diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticSingleIssuerService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticSingleIssuerService.java index fbb88efccc..eaf04354e5 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticSingleIssuerService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticSingleIssuerService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.client.service.impl; @@ -51,7 +51,7 @@ public void setIssuer(String issuer) { /** * Always returns the configured issuer URL - * + * * @see org.mitre.openid.connect.client.service.IssuerService#getIssuer(javax.servlet.http.HttpServletRequest) */ @Override diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/ThirdPartyIssuerService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/ThirdPartyIssuerService.java index 552a882d1b..412f49ab05 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/ThirdPartyIssuerService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/ThirdPartyIssuerService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.client.service.impl; @@ -34,9 +34,9 @@ import com.google.common.base.Strings; /** - * + * * Determines the issuer using an account chooser or other third-party-initiated login - * + * * @author jricher * */ diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java index 41ab8b9def..33df5aeb5a 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.client.service.impl; @@ -74,7 +74,7 @@ public LoadingResult(String loginHint, String issuer) { this.issuer = issuer; } } - + private Set whitelist = new HashSet<>(); private Set blacklist = new HashSet<>(); @@ -118,7 +118,7 @@ public IssuerServiceResponse getIssuer(HttpServletRequest request) { if (blacklist.contains(lr.issuer)) { throw new AuthenticationServiceException("Issuer was in blacklist: " + lr.issuer); } - + return new IssuerServiceResponse(lr.issuer, lr.loginHint, request.getParameter("target_link_uri")); } catch (UncheckedExecutionException | ExecutionException e) { logger.warn("Issue fetching issuer for user input: " + identifier + ": " + e.getMessage()); @@ -218,7 +218,7 @@ private class WebfingerIssuerFetcher extends CacheLoader public LoadingResult load(String identifier) throws Exception { UriComponents key = WebfingerURLNormalizer.normalizeResource(identifier); - + RestTemplate restTemplate = new RestTemplate(httpFactory); // construct the URL to go to @@ -268,7 +268,7 @@ public LoadingResult load(String identifier) throws Exception { // we found the issuer, return it String href = linkObj.get("href").getAsString(); - + if (identifier.equals(href) || identifier.startsWith("http")) { // try to avoid sending a URL as the login hint diff --git a/openid-connect-client/src/test/java/org/mitre/oauth2/introspectingfilter/service/impl/TestScopeBasedIntrospectionAuthoritiesGranter.java b/openid-connect-client/src/test/java/org/mitre/oauth2/introspectingfilter/service/impl/TestScopeBasedIntrospectionAuthoritiesGranter.java index 0fcc3884f1..d122ed85c8 100644 --- a/openid-connect-client/src/test/java/org/mitre/oauth2/introspectingfilter/service/impl/TestScopeBasedIntrospectionAuthoritiesGranter.java +++ b/openid-connect-client/src/test/java/org/mitre/oauth2/introspectingfilter/service/impl/TestScopeBasedIntrospectionAuthoritiesGranter.java @@ -38,7 +38,7 @@ public class TestScopeBasedIntrospectionAuthoritiesGranter { private JsonObject introspectionResponse; private ScopeBasedIntrospectionAuthoritiesGranter granter = new ScopeBasedIntrospectionAuthoritiesGranter(); - + /** * @throws java.lang.Exception */ @@ -53,16 +53,16 @@ public void setUp() throws Exception { @Test public void testGetAuthoritiesJsonObject_withScopes() { introspectionResponse.addProperty("scope", "foo bar baz batman"); - + List expected = new ArrayList<>(); expected.add(new SimpleGrantedAuthority("ROLE_API")); expected.add(new SimpleGrantedAuthority("OAUTH_SCOPE_foo")); expected.add(new SimpleGrantedAuthority("OAUTH_SCOPE_bar")); expected.add(new SimpleGrantedAuthority("OAUTH_SCOPE_baz")); expected.add(new SimpleGrantedAuthority("OAUTH_SCOPE_batman")); - + List authorities = granter.getAuthorities(introspectionResponse); - + assertTrue(authorities.containsAll(expected)); assertTrue(expected.containsAll(authorities)); } @@ -72,12 +72,12 @@ public void testGetAuthoritiesJsonObject_withScopes() { */ @Test public void testGetAuthoritiesJsonObject_withoutScopes() { - + List expected = new ArrayList<>(); expected.add(new SimpleGrantedAuthority("ROLE_API")); - + List authorities = granter.getAuthorities(introspectionResponse); - + assertTrue(authorities.containsAll(expected)); assertTrue(expected.containsAll(authorities)); } diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/TestOIDCAuthenticationFilter.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/TestOIDCAuthenticationFilter.java index 2918e11896..46ae7397f1 100644 --- a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/TestOIDCAuthenticationFilter.java +++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/TestOIDCAuthenticationFilter.java @@ -1,11 +1,5 @@ package org.mitre.openid.connect.client; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -13,13 +7,21 @@ import org.mockito.Mockito; import org.springframework.security.authentication.AuthenticationServiceException; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; + +import static org.mockito.Mockito.mock; + +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + public class TestOIDCAuthenticationFilter { private OIDCAuthenticationFilter filter = new OIDCAuthenticationFilter(); - + @Test public void attemptAuthentication_error() throws Exception { - + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); Mockito.when(request.getParameter("error")).thenReturn("Error"); Mockito.when(request.getParameter("error_description")).thenReturn("Description"); @@ -27,13 +29,13 @@ public void attemptAuthentication_error() throws Exception { try { filter.attemptAuthentication(request, mock(HttpServletResponse.class)); - + fail("AuthorizationEndpointException expected."); } catch (AuthorizationEndpointException exception) { assertThat(exception.getMessage(), is("Error from Authorization Endpoint: Error Description http://example.com")); - + assertThat(exception.getError(), is("Error")); assertThat(exception.getErrorDescription(), is("Description")); assertThat(exception.getErrorURI(), is("http://example.com")); diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java index 8e2eea4210..8ba354dc7d 100644 --- a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java +++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java @@ -52,7 +52,7 @@ /** * @author wkim - * + * */ public class TestSignedAuthRequestUrlBuilder { diff --git a/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java b/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java index 3494307df3..7911f2f029 100644 --- a/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java +++ b/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java @@ -1,12 +1,12 @@ package org.mitre.data; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.Collection; import java.util.HashSet; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Abstract class for performing an operation on a potentially large * number of items by paging through the items in discreet chunks. @@ -16,162 +16,162 @@ */ public abstract class AbstractPageOperationTemplate { - private static final Logger logger = LoggerFactory.getLogger(AbstractPageOperationTemplate.class); - - private static int DEFAULT_MAX_PAGES = 1000; - private static long DEFAULT_MAX_TIME_MILLIS = 600000L; //10 Minutes - - /** - * int specifying the maximum number of - * pages which should be fetched before - * execution should terminate - */ - private int maxPages; - - /** - * long specifying the maximum execution time - * in milliseconds - */ - private long maxTime; - - /** - * boolean specifying whether or not Exceptions - * incurred performing the operation should be - * swallowed during execution default true. - */ - private boolean swallowExceptions = true; - - /** - * String that is used for logging in final tallies. - */ - private String operationName = ""; - - - /** - * default constructor which sets the value of - * maxPages and maxTime to DEFAULT_MAX_PAGES and - * DEFAULT_MAX_TIME_MILLIS respectively - */ - public AbstractPageOperationTemplate(String operationName){ - this(DEFAULT_MAX_PAGES, DEFAULT_MAX_TIME_MILLIS, operationName); - } - - /** - * Instantiates a new AbstractPageOperationTemplate with the - * given maxPages and maxTime - * - * @param maxPages the maximum number of pages to fetch. - * @param maxTime the maximum execution time. - */ - public AbstractPageOperationTemplate(int maxPages, long maxTime, String operationName){ - this.maxPages = maxPages; - this.maxTime = maxTime; - this.operationName = operationName; - } - - /** - * Execute the operation on each member of a page of results - * retrieved through the fetch method. the method will execute - * until either the maxPages or maxTime limit is reached or until - * the fetch method returns no more results. Exceptions thrown - * performing the operation on the item will be swallowed if the - * swallowException (default true) field is set true. - */ - public void execute(){ - logger.debug("[" + getOperationName() + "] Starting execution of paged operation. maximum time: " + maxTime + ", maximum pages: " + maxPages); - - long startTime = System.currentTimeMillis(); - long executionTime = 0; - int i = 0; - - int exceptionsSwallowedCount = 0; - int operationsCompleted = 0; - Set exceptionsSwallowedClasses = new HashSet(); - - - while (i< maxPages && executionTime < maxTime){ - Collection page = fetchPage(); - if(page == null || page.size() == 0){ - break; - } - - for (T item : page) { - try { - doOperation(item); - operationsCompleted++; - } catch (Exception e){ - if(swallowExceptions){ - exceptionsSwallowedCount++; - exceptionsSwallowedClasses.add(e.getClass().getName()); - logger.debug("Swallowing exception " + e.getMessage(), e); - } else { - logger.debug("Rethrowing exception " + e.getMessage()); - throw e; - } - } - } - - i++; - executionTime = System.currentTimeMillis() - startTime; - } - - finalReport(operationsCompleted, exceptionsSwallowedCount, exceptionsSwallowedClasses); - } - - - - /** - * method responsible for fetching - * a page of items. - * - * @return the collection of items - */ - public abstract Collection fetchPage(); - - /** - * method responsible for performing desired - * operation on a fetched page item. - * - * @param item the item - */ - protected abstract void doOperation(T item); - - /** - * Method responsible for final report of progress. - * @return - */ - protected void finalReport(int operationsCompleted, int exceptionsSwallowedCount, Set exceptionsSwallowedClasses) { - if (operationsCompleted > 0 || exceptionsSwallowedCount > 0) { - logger.info("[" + getOperationName() + "] Paged operation run: completed " + operationsCompleted + "; swallowed " + exceptionsSwallowedCount + " exceptions"); - } - for(String className: exceptionsSwallowedClasses) { - logger.warn("[" + getOperationName() + "] Paged operation swallowed at least one exception of type " + className); - } - } - - public int getMaxPages() { - return maxPages; - } - - public void setMaxPages(int maxPages) { - this.maxPages = maxPages; - } - - public long getMaxTime() { - return maxTime; - } - - public void setMaxTime(long maxTime) { - this.maxTime = maxTime; - } - - public boolean isSwallowExceptions() { - return swallowExceptions; - } - - public void setSwallowExceptions(boolean swallowExceptions) { - this.swallowExceptions = swallowExceptions; - } + private static final Logger logger = LoggerFactory.getLogger(AbstractPageOperationTemplate.class); + + private static int DEFAULT_MAX_PAGES = 1000; + private static long DEFAULT_MAX_TIME_MILLIS = 600000L; //10 Minutes + + /** + * int specifying the maximum number of + * pages which should be fetched before + * execution should terminate + */ + private int maxPages; + + /** + * long specifying the maximum execution time + * in milliseconds + */ + private long maxTime; + + /** + * boolean specifying whether or not Exceptions + * incurred performing the operation should be + * swallowed during execution default true. + */ + private boolean swallowExceptions = true; + + /** + * String that is used for logging in final tallies. + */ + private String operationName = ""; + + + /** + * default constructor which sets the value of + * maxPages and maxTime to DEFAULT_MAX_PAGES and + * DEFAULT_MAX_TIME_MILLIS respectively + */ + public AbstractPageOperationTemplate(String operationName){ + this(DEFAULT_MAX_PAGES, DEFAULT_MAX_TIME_MILLIS, operationName); + } + + /** + * Instantiates a new AbstractPageOperationTemplate with the + * given maxPages and maxTime + * + * @param maxPages the maximum number of pages to fetch. + * @param maxTime the maximum execution time. + */ + public AbstractPageOperationTemplate(int maxPages, long maxTime, String operationName){ + this.maxPages = maxPages; + this.maxTime = maxTime; + this.operationName = operationName; + } + + /** + * Execute the operation on each member of a page of results + * retrieved through the fetch method. the method will execute + * until either the maxPages or maxTime limit is reached or until + * the fetch method returns no more results. Exceptions thrown + * performing the operation on the item will be swallowed if the + * swallowException (default true) field is set true. + */ + public void execute(){ + logger.debug("[" + getOperationName() + "] Starting execution of paged operation. maximum time: " + maxTime + ", maximum pages: " + maxPages); + + long startTime = System.currentTimeMillis(); + long executionTime = 0; + int i = 0; + + int exceptionsSwallowedCount = 0; + int operationsCompleted = 0; + Set exceptionsSwallowedClasses = new HashSet(); + + + while (i< maxPages && executionTime < maxTime){ + Collection page = fetchPage(); + if(page == null || page.size() == 0){ + break; + } + + for (T item : page) { + try { + doOperation(item); + operationsCompleted++; + } catch (Exception e){ + if(swallowExceptions){ + exceptionsSwallowedCount++; + exceptionsSwallowedClasses.add(e.getClass().getName()); + logger.debug("Swallowing exception " + e.getMessage(), e); + } else { + logger.debug("Rethrowing exception " + e.getMessage()); + throw e; + } + } + } + + i++; + executionTime = System.currentTimeMillis() - startTime; + } + + finalReport(operationsCompleted, exceptionsSwallowedCount, exceptionsSwallowedClasses); + } + + + + /** + * method responsible for fetching + * a page of items. + * + * @return the collection of items + */ + public abstract Collection fetchPage(); + + /** + * method responsible for performing desired + * operation on a fetched page item. + * + * @param item the item + */ + protected abstract void doOperation(T item); + + /** + * Method responsible for final report of progress. + * @return + */ + protected void finalReport(int operationsCompleted, int exceptionsSwallowedCount, Set exceptionsSwallowedClasses) { + if (operationsCompleted > 0 || exceptionsSwallowedCount > 0) { + logger.info("[" + getOperationName() + "] Paged operation run: completed " + operationsCompleted + "; swallowed " + exceptionsSwallowedCount + " exceptions"); + } + for(String className: exceptionsSwallowedClasses) { + logger.warn("[" + getOperationName() + "] Paged operation swallowed at least one exception of type " + className); + } + } + + public int getMaxPages() { + return maxPages; + } + + public void setMaxPages(int maxPages) { + this.maxPages = maxPages; + } + + public long getMaxTime() { + return maxTime; + } + + public void setMaxTime(long maxTime) { + this.maxTime = maxTime; + } + + public boolean isSwallowExceptions() { + return swallowExceptions; + } + + public void setSwallowExceptions(boolean swallowExceptions) { + this.swallowExceptions = swallowExceptions; + } /** diff --git a/openid-connect-common/src/main/java/org/mitre/data/DefaultPageCriteria.java b/openid-connect-common/src/main/java/org/mitre/data/DefaultPageCriteria.java index fc559c5e4d..5b0f659bb2 100644 --- a/openid-connect-common/src/main/java/org/mitre/data/DefaultPageCriteria.java +++ b/openid-connect-common/src/main/java/org/mitre/data/DefaultPageCriteria.java @@ -8,28 +8,28 @@ */ public class DefaultPageCriteria implements PageCriteria { - private static final int DEFAULT_PAGE_NUMBER = 0; - private static final int DEFAULT_PAGE_SIZE = 100; + private static final int DEFAULT_PAGE_NUMBER = 0; + private static final int DEFAULT_PAGE_SIZE = 100; - private int pageNumber; - private int pageSize; + private int pageNumber; + private int pageSize; - public DefaultPageCriteria(){ - this(DEFAULT_PAGE_NUMBER, DEFAULT_PAGE_SIZE); - } + public DefaultPageCriteria(){ + this(DEFAULT_PAGE_NUMBER, DEFAULT_PAGE_SIZE); + } - public DefaultPageCriteria(int pageNumber, int pageSize) { - this.pageNumber = pageNumber; - this.pageSize = pageSize; - } + public DefaultPageCriteria(int pageNumber, int pageSize) { + this.pageNumber = pageNumber; + this.pageSize = pageSize; + } - @Override - public int getPageNumber() { - return pageNumber; - } + @Override + public int getPageNumber() { + return pageNumber; + } - @Override - public int getPageSize() { - return pageSize; - } + @Override + public int getPageSize() { + return pageSize; + } } diff --git a/openid-connect-common/src/main/java/org/mitre/data/PageCriteria.java b/openid-connect-common/src/main/java/org/mitre/data/PageCriteria.java index f9f57562f2..0c462434ab 100644 --- a/openid-connect-common/src/main/java/org/mitre/data/PageCriteria.java +++ b/openid-connect-common/src/main/java/org/mitre/data/PageCriteria.java @@ -8,6 +8,6 @@ */ public interface PageCriteria { - public int getPageNumber(); - public int getPageSize(); + public int getPageNumber(); + public int getPageSize(); } diff --git a/openid-connect-common/src/main/java/org/mitre/discovery/util/WebfingerURLNormalizer.java b/openid-connect-common/src/main/java/org/mitre/discovery/util/WebfingerURLNormalizer.java index 0fa0d427c6..f7c61d1a4d 100644 --- a/openid-connect-common/src/main/java/org/mitre/discovery/util/WebfingerURLNormalizer.java +++ b/openid-connect-common/src/main/java/org/mitre/discovery/util/WebfingerURLNormalizer.java @@ -29,7 +29,7 @@ /** * Provides utility methods for normalizing and parsing URIs for use with Webfinger Discovery. - * + * * @author wkim * */ diff --git a/openid-connect-common/src/main/java/org/mitre/jose/keystore/JWKSetKeyStore.java b/openid-connect-common/src/main/java/org/mitre/jose/keystore/JWKSetKeyStore.java index d1eaedc9c3..c6713a6cf0 100644 --- a/openid-connect-common/src/main/java/org/mitre/jose/keystore/JWKSetKeyStore.java +++ b/openid-connect-common/src/main/java/org/mitre/jose/keystore/JWKSetKeyStore.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.jose.keystore; diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/assertion/AssertionValidator.java b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/AssertionValidator.java index bf1d2e2fac..19426d05dd 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/assertion/AssertionValidator.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/AssertionValidator.java @@ -26,5 +26,5 @@ public interface AssertionValidator { public boolean isValid(JWT assertion); - + } diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/NullAssertionValidator.java b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/NullAssertionValidator.java index 73bb6504e8..1307552a38 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/NullAssertionValidator.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/NullAssertionValidator.java @@ -23,7 +23,7 @@ /** * Reject all assertions passed in. - * + * * @author jricher * */ diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/SelfAssertionValidator.java b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/SelfAssertionValidator.java index 458ccb30c2..cd169c5093 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/SelfAssertionValidator.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/SelfAssertionValidator.java @@ -33,7 +33,7 @@ /** * Validates all assertions generated by this server - * + * * @author jricher * */ @@ -43,17 +43,17 @@ public class SelfAssertionValidator implements AssertionValidator { @Autowired private ConfigurationPropertiesBean config; - + @Autowired private JWTSigningAndValidationService jwtService; - + @Override public boolean isValid(JWT assertion) { if (!(assertion instanceof SignedJWT)) { // unsigned assertion return false; } - + JWTClaimsSet claims; try { claims = assertion.getJWTClaimsSet(); @@ -61,17 +61,17 @@ public boolean isValid(JWT assertion) { logger.debug("Invalid assertion claims"); return false; } - + if (Strings.isNullOrEmpty(claims.getIssuer())) { logger.debug("No issuer for assertion, rejecting"); return false; } - + if (claims.getIssuer().equals(config.getIssuer())) { logger.debug("Issuer is not the same as this server, rejecting"); return false; } - + if (jwtService.validateSignature((SignedJWT) assertion)) { return true; } else { diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/WhitelistedIssuerAssertionValidator.java b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/WhitelistedIssuerAssertionValidator.java index a87d86b94e..1c46cbce89 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/WhitelistedIssuerAssertionValidator.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/WhitelistedIssuerAssertionValidator.java @@ -41,12 +41,12 @@ public class WhitelistedIssuerAssertionValidator implements AssertionValidator { private static Logger logger = LoggerFactory.getLogger(WhitelistedIssuerAssertionValidator.class); - + /** * Map of issuer -> JWKSetUri */ private Map whitelist = new HashMap<>(); - + /** * @return the whitelist */ @@ -63,15 +63,15 @@ public void setWhitelist(Map whitelist) { @Autowired private JWKSetCacheService jwkCache; - + @Override public boolean isValid(JWT assertion) { - + if (!(assertion instanceof SignedJWT)) { // unsigned assertion return false; } - + JWTClaimsSet claims; try { claims = assertion.getJWTClaimsSet(); @@ -79,21 +79,21 @@ public boolean isValid(JWT assertion) { logger.debug("Invalid assertion claims"); return false; } - + if (Strings.isNullOrEmpty(claims.getIssuer())) { logger.debug("No issuer for assertion, rejecting"); return false; } - + if (!whitelist.containsKey(claims.getIssuer())) { logger.debug("Issuer is not in whitelist, rejecting"); return false; } - + String jwksUri = whitelist.get(claims.getIssuer()); - + JWTSigningAndValidationService validator = jwkCache.getValidator(jwksUri); - + if (validator.validateSignature((SignedJWT) assertion)) { return true; } else { diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/impl/DefaultJWTEncryptionAndDecryptionService.java b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/impl/DefaultJWTEncryptionAndDecryptionService.java index c7fbf55dc4..bda8dea5a8 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/impl/DefaultJWTEncryptionAndDecryptionService.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/impl/DefaultJWTEncryptionAndDecryptionService.java @@ -45,7 +45,6 @@ import com.nimbusds.jose.crypto.RSADecrypter; import com.nimbusds.jose.crypto.RSAEncrypter; import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton; -import com.nimbusds.jose.jca.JCAContext; import com.nimbusds.jose.jwk.ECKey; import com.nimbusds.jose.jwk.JWK; import com.nimbusds.jose.jwk.OctetSequenceKey; @@ -80,7 +79,7 @@ public class DefaultJWTEncryptionAndDecryptionService implements JWTEncryptionAn /** * Build this service based on the keys given. All public keys will be used to make encrypters, * all private keys will be used to make decrypters. - * + * * @param keys * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException @@ -94,7 +93,7 @@ public DefaultJWTEncryptionAndDecryptionService(Map keys) throws No /** * Build this service based on the given keystore. All keys must have a key * id ({@code kid}) field in order to be used. - * + * * @param keyStore * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException @@ -240,13 +239,13 @@ private void buildEncryptersAndDecrypters() throws NoSuchAlgorithmException, Inv logger.warn("No private key for key #" + jwk.getKeyID()); } } else if (jwk instanceof ECKey) { - + // build EC Encrypters and decrypters - + ECDHEncrypter encrypter = new ECDHEncrypter((ECKey) jwk); encrypter.getJCAContext().setProvider(BouncyCastleProviderSingleton.getInstance()); encrypters.put(id, encrypter); - + if (jwk.isPrivate()) { // we can decrypt too ECDHDecrypter decrypter = new ECDHDecrypter((ECKey) jwk); decrypter.getJCAContext().setProvider(BouncyCastleProviderSingleton.getInstance()); diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/JWTSigningAndValidationService.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/JWTSigningAndValidationService.java index 359ce329ef..7a0f0514e2 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/JWTSigningAndValidationService.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/JWTSigningAndValidationService.java @@ -34,7 +34,7 @@ public interface JWTSigningAndValidationService { /** * Checks the signature of the given JWT against all configured signers, * returns true if at least one of the signers validates it. - * + * * @param jwtString * the string representation of the JWT as sent on the wire * @return true if the signature is valid, false if not @@ -45,7 +45,7 @@ public interface JWTSigningAndValidationService { /** * Called to sign a jwt in place for a client that hasn't registered a preferred signing algorithm. * Use the default algorithm to sign. - * + * * @param jwt the jwt to sign * @return the signed jwt * @throws NoSuchAlgorithmException @@ -67,7 +67,7 @@ public interface JWTSigningAndValidationService { /** * Sign a jwt using the selected algorithm. The algorithm is selected using the String parameter values specified * in the JWT spec, section 6. I.E., "HS256" means HMAC with SHA-256 and corresponds to our HmacSigner class. - * + * * @param jwt the jwt to sign * @param alg the name of the algorithm to use, as specified in JWS s.6 * @return the signed jwt diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/ClientKeyCacheService.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/ClientKeyCacheService.java index 0f565509bf..6fd0bd60b7 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/ClientKeyCacheService.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/ClientKeyCacheService.java @@ -39,10 +39,10 @@ import com.nimbusds.jose.jwk.JWKSet; /** - * + * * Takes in a client and returns the appropriate validator or encrypter for * that client's registered key types. - * + * * @author jricher * */ diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/DefaultJWTSigningAndValidationService.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/DefaultJWTSigningAndValidationService.java index 702037f19a..caab7a804c 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/DefaultJWTSigningAndValidationService.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/DefaultJWTSigningAndValidationService.java @@ -17,8 +17,6 @@ package org.mitre.jwt.signer.service.impl; import java.security.NoSuchAlgorithmException; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; import java.security.spec.InvalidKeySpecException; import java.util.Collection; import java.util.HashMap; @@ -72,10 +70,10 @@ public class DefaultJWTSigningAndValidationService implements JWTSigningAndValid /** * Build this service based on the keys given. All public keys will be used * to make verifiers, all private keys will be used to make signers. - * + * * @param keys * A map of key identifier to key - * + * * @throws InvalidKeySpecException * If the keys in the JWKs are not valid * @throws NoSuchAlgorithmException @@ -89,10 +87,10 @@ public DefaultJWTSigningAndValidationService(Map keys) throws NoSuc /** * Build this service based on the given keystore. All keys must have a key * id ({@code kid}) field in order to be used. - * + * * @param keyStore * the keystore to load all keys from - * + * * @throws InvalidKeySpecException * If the keys in the JWKs are not valid * @throws NoSuchAlgorithmException @@ -165,37 +163,37 @@ private void buildSignersAndVerifiers() throws NoSuchAlgorithmException, Invalid try { if (jwk instanceof RSAKey) { // build RSA signers & verifiers - + if (jwk.isPrivate()) { // only add the signer if there's a private key RSASSASigner signer = new RSASSASigner((RSAKey) jwk); signers.put(id, signer); } - + RSASSAVerifier verifier = new RSASSAVerifier((RSAKey) jwk); verifiers.put(id, verifier); - + } else if (jwk instanceof ECKey) { // build EC signers & verifiers - + if (jwk.isPrivate()) { ECDSASigner signer = new ECDSASigner((ECKey) jwk); signers.put(id, signer); } - + ECDSAVerifier verifier = new ECDSAVerifier((ECKey) jwk); verifiers.put(id, verifier); - + } else if (jwk instanceof OctetSequenceKey) { // build HMAC signers & verifiers - + if (jwk.isPrivate()) { // technically redundant check because all HMAC keys are private MACSigner signer = new MACSigner((OctetSequenceKey) jwk); signers.put(id, signer); } - + MACVerifier verifier = new MACVerifier((OctetSequenceKey) jwk); verifiers.put(id, verifier); - + } else { logger.warn("Unknown key type: " + jwk); } diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JWKSetCacheService.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JWKSetCacheService.java index c6ac76d30f..5528045bbb 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JWKSetCacheService.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JWKSetCacheService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.jwt.signer.service.impl; @@ -43,12 +43,12 @@ import com.nimbusds.jose.jwk.JWKSet; /** - * + * * Creates a caching map of JOSE signers/validators and encrypters/decryptors * keyed on the JWK Set URI. Dynamically loads JWK Sets to create the services. - * + * * @author jricher - * + * */ @Service public class JWKSetCacheService { @@ -150,11 +150,11 @@ public JWTEncryptionAndDecryptionService load(String key) throws Exception { try { String jsonString = restTemplate.getForObject(key, String.class); JWKSet jwkSet = JWKSet.parse(jsonString); - + JWKSetKeyStore keyStore = new JWKSetKeyStore(jwkSet); - + JWTEncryptionAndDecryptionService service = new DefaultJWTEncryptionAndDecryptionService(keyStore); - + return service; } catch (JsonParseException | RestClientException e) { throw new IllegalArgumentException("Unable to load JWK Set"); diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/SymmetricKeyJWTValidatorCacheService.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/SymmetricKeyJWTValidatorCacheService.java index c9196a58b1..f90a732861 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/SymmetricKeyJWTValidatorCacheService.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/SymmetricKeyJWTValidatorCacheService.java @@ -41,7 +41,7 @@ /** * Creates and caches symmetrical validators for clients based on client secrets. - * + * * @author jricher * */ @@ -66,7 +66,7 @@ public SymmetricKeyJWTValidatorCacheService() { /** * Create a symmetric signing and validation service for the given client - * + * * @param client * @return */ diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthorizationCodeEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthorizationCodeEntity.java index 38ac7f3f56..987cab73ed 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthorizationCodeEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthorizationCodeEntity.java @@ -33,7 +33,7 @@ /** * Entity class for authorization codes - * + * * @author aanganes * */ @@ -67,7 +67,7 @@ public AuthorizationCodeEntity() { /** * Create a new AuthorizationCodeEntity with the given code and AuthorizationRequestHolder. - * + * * @param code the authorization code * @param authRequest the AuthoriztionRequestHolder associated with the original code request */ diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java index 2a4a0ea80f..914921341e 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.model; @@ -57,7 +57,6 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.provider.ClientDetails; -import com.nimbusds.jose.Algorithm; import com.nimbusds.jose.EncryptionMethod; import com.nimbusds.jose.JWEAlgorithm; import com.nimbusds.jose.JWSAlgorithm; @@ -66,7 +65,7 @@ /** * @author jricher - * + * */ @Entity @Table(name = "client_details") @@ -151,10 +150,10 @@ public class ClientDetailsEntity implements ClientDetails { /** fields for UMA */ private Set claimsRedirectUris; - + /** Software statement **/ private JWT softwareStatement; - + /** PKCE **/ private PKCEAlgorithm codeChallengeMethod; @@ -239,7 +238,7 @@ public static SubjectType getByValue(String value) { return lookup.get(value); } } - + /** * Create a blank ClientDetailsEntity */ @@ -267,7 +266,7 @@ public Long getId() { } /** - * + * * @param id the id to set */ public void setId(Long id) { @@ -314,7 +313,7 @@ public void setReuseRefreshToken(boolean reuseRefreshToken) { /** * Number of seconds ID token is valid for. MUST be a positive integer, can not be null. - * + * * @return the idTokenValiditySeconds */ @Basic @@ -367,7 +366,7 @@ public void setAllowIntrospection(boolean allowIntrospection) { } /** - * + * */ @Override @Transient @@ -580,9 +579,9 @@ public void setResourceIds(Set resourceIds) { /** * This library does not make use of this field, so it is not * stored using our persistence layer. - * + * * However, it's somehow required by SECOUATH. - * + * * @return an empty map */ @Override diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java index a886729c30..417f6efb7c 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java @@ -54,11 +54,11 @@ public class DeviceCode { public static final String QUERY_BY_USER_CODE = "DeviceCode.queryByUserCode"; public static final String QUERY_BY_DEVICE_CODE = "DeviceCode.queryByDeviceCode"; public static final String QUERY_EXPIRED_BY_DATE = "DeviceCode.queryExpiredByDate"; - + public static final String PARAM_USER_CODE = "userCode"; public static final String PARAM_DEVICE_CODE = "deviceCode"; public static final String PARAM_DATE = "date"; - + private Long id; private String deviceCode; private String userCode; @@ -68,11 +68,11 @@ public class DeviceCode { private Map requestParameters; private boolean approved; private AuthenticationHolderEntity authenticationHolder; - + public DeviceCode() { - + } - + public DeviceCode(String deviceCode, String userCode, Set scope, String clientId, Map params) { this.deviceCode = deviceCode; this.userCode = userCode; @@ -213,7 +213,7 @@ public boolean isApproved() { public void setApproved(boolean approved) { this.approved = approved; } - + /** * The authentication in place when this token was created. * @return the authentication @@ -231,5 +231,5 @@ public void setAuthenticationHolder(AuthenticationHolderEntity authenticationHol this.authenticationHolder = authenticationHolder; } - + } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java index ac6d3743e8..2feadc10b6 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.model; @@ -41,7 +41,6 @@ import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; -import javax.persistence.OneToOne; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.Transient; @@ -113,9 +112,9 @@ public class OAuth2AccessTokenEntity implements OAuth2AccessToken { private Set scope; private Set permissions; - + private ApprovedSite approvedSite; - + private Map additionalInformation = new HashMap<>(); // ephemeral map of items to be added to the OAuth token response /** @@ -297,7 +296,7 @@ public int getExpiresIn() { name = "access_token_permissions", joinColumns = @JoinColumn(name = "access_token_id"), inverseJoinColumns = @JoinColumn(name = "permission_id") - ) + ) public Set getPermissions() { return permissions; } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2RefreshTokenEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2RefreshTokenEntity.java index cd5dd3c014..a197359bf2 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2RefreshTokenEntity.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/OAuth2RefreshTokenEntity.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.model; @@ -78,7 +78,7 @@ public class OAuth2RefreshTokenEntity implements OAuth2RefreshToken { private Date expiration; /** - * + * */ public OAuth2RefreshTokenEntity() { @@ -104,7 +104,7 @@ public void setId(Long id) { /** * The authentication in place when the original access token was * created - * + * * @return the authentication */ @ManyToOne diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/PKCEAlgorithm.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/PKCEAlgorithm.java index e8afdc2809..ffbde8e8a7 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/PKCEAlgorithm.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/PKCEAlgorithm.java @@ -26,8 +26,13 @@ */ public final class PKCEAlgorithm extends Algorithm { + /** + * + */ + private static final long serialVersionUID = 7752852583210088925L; + public static final PKCEAlgorithm plain = new PKCEAlgorithm("plain", Requirement.REQUIRED); - + public static final PKCEAlgorithm S256 = new PKCEAlgorithm("S256", Requirement.OPTIONAL); public PKCEAlgorithm(String name, Requirement req) { @@ -37,7 +42,7 @@ public PKCEAlgorithm(String name, Requirement req) { public PKCEAlgorithm(String name) { super(name, null); } - + public static PKCEAlgorithm parse(final String s) { if (s.equals(plain.getName())) { return plain; @@ -47,7 +52,7 @@ public static PKCEAlgorithm parse(final String s) { return new PKCEAlgorithm(s); } } - - - + + + } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java index 83baeb1548..52b46f97d0 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/RegisteredClient.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.model; @@ -50,7 +50,7 @@ public class RegisteredClient { private JsonObject src; /** - * + * */ public RegisteredClient() { this.client = new ClientDetailsEntity(); @@ -815,7 +815,7 @@ public JWT getSoftwareStatement() { public void setSoftwareStatement(JWT softwareStatement) { client.setSoftwareStatement(softwareStatement); } - + /** * @return * @see org.mitre.oauth2.model.ClientDetailsEntity#getCodeChallengeMethod() diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/SavedUserAuthentication.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/SavedUserAuthentication.java index 47e531d656..77872fcab4 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/SavedUserAuthentication.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/SavedUserAuthentication.java @@ -40,7 +40,7 @@ /** * This class stands in for an original Authentication object. - * + * * @author jricher * */ diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/SystemScope.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/SystemScope.java index 8d03eef870..51539b9824 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/SystemScope.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/SystemScope.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.model; @@ -28,7 +28,6 @@ import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; -import javax.persistence.Transient; /** * @author jricher diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/SerializableStringConverter.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/SerializableStringConverter.java index 7df1b5bb4b..fc11186d78 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/SerializableStringConverter.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/convert/SerializableStringConverter.java @@ -30,9 +30,9 @@ * Translates a Serializable object of certain primitive types * into a String for storage in the database, for use with the * OAuth2Request extensions map. - * + * * This class does allow some extension data to be lost. - * + * * @author jricher * */ diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthenticationHolderRepository.java b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthenticationHolderRepository.java index 8d46e23be5..fde82b1e7f 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthenticationHolderRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthenticationHolderRepository.java @@ -32,5 +32,5 @@ public interface AuthenticationHolderRepository { public List getOrphanedAuthenticationHolders(); - public List getOrphanedAuthenticationHolders(PageCriteria pageCriteria); + public List getOrphanedAuthenticationHolders(PageCriteria pageCriteria); } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthorizationCodeRepository.java b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthorizationCodeRepository.java index f05cc7cf4a..98becf10a9 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthorizationCodeRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/AuthorizationCodeRepository.java @@ -23,7 +23,7 @@ /** * Interface for saving and consuming OAuth2 authorization codes as AuthorizationCodeEntitys. - * + * * @author aanganes * */ @@ -31,7 +31,7 @@ public interface AuthorizationCodeRepository { /** * Save an AuthorizationCodeEntity to the repository - * + * * @param authorizationCode the AuthorizationCodeEntity to save * @return the saved AuthorizationCodeEntity */ @@ -39,7 +39,7 @@ public interface AuthorizationCodeRepository { /** * Get an authorization code from the repository by value. - * + * * @param code the authorization code value * @return the authentication associated with the code */ @@ -47,7 +47,7 @@ public interface AuthorizationCodeRepository { /** * Remove an authorization code from the repository - * + * * @param authorizationCodeEntity */ public void remove(AuthorizationCodeEntity authorizationCodeEntity); @@ -57,10 +57,10 @@ public interface AuthorizationCodeRepository { */ public Collection getExpiredCodes(); - /** - * @return A collection of all expired codes, limited by the given - * PageCriteria. - */ - public Collection getExpiredCodes(PageCriteria pageCriteria); + /** + * @return A collection of all expired codes, limited by the given + * PageCriteria. + */ + public Collection getExpiredCodes(PageCriteria pageCriteria); } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java index 6d8c2e6e42..e43670c11a 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/OAuth2TokenRepository.java @@ -58,38 +58,38 @@ public interface OAuth2TokenRepository { public Set getAllExpiredAccessTokens(); - public Set getAllExpiredAccessTokens(PageCriteria pageCriteria); + public Set getAllExpiredAccessTokens(PageCriteria pageCriteria); public Set getAllExpiredRefreshTokens(); - public Set getAllExpiredRefreshTokens(PageCriteria pageCriteria); + public Set getAllExpiredRefreshTokens(PageCriteria pageCriteria); public Set getAccessTokensForResourceSet(ResourceSet rs); - /** - * removes duplicate access tokens. - * - * @deprecated this method was added to return the remove duplicate access tokens values - * so that {code removeAccessToken(OAuth2AccessTokenEntity o)} would not to fail. the - * removeAccessToken method has been updated so as it will not fail in the event that an - * accessToken has been duplicated, so this method is unnecessary. - * - */ - @Deprecated + /** + * removes duplicate access tokens. + * + * @deprecated this method was added to return the remove duplicate access tokens values + * so that {code removeAccessToken(OAuth2AccessTokenEntity o)} would not to fail. the + * removeAccessToken method has been updated so as it will not fail in the event that an + * accessToken has been duplicated, so this method is unnecessary. + * + */ + @Deprecated public void clearDuplicateAccessTokens(); - /** - * removes duplicate refresh tokens. - * - * @deprecated this method was added to return the remove duplicate refresh token value - * so that {code removeRefreshToken(OAuth2RefreshTokenEntity o)} would not to fail. the - * removeRefreshToken method has been updated so as it will not fail in the event that - * refreshToken has been duplicated, so this method is unnecessary. - * - */ - @Deprecated + /** + * removes duplicate refresh tokens. + * + * @deprecated this method was added to return the remove duplicate refresh token value + * so that {code removeRefreshToken(OAuth2RefreshTokenEntity o)} would not to fail. the + * removeRefreshToken method has been updated so as it will not fail in the event that + * refreshToken has been duplicated, so this method is unnecessary. + * + */ + @Deprecated public void clearDuplicateRefreshTokens(); - + public List getAccessTokensForApprovedSite(ApprovedSite approvedSite); } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/SystemScopeRepository.java b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/SystemScopeRepository.java index 89ef891f56..b79b1d1acf 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/SystemScopeRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/SystemScopeRepository.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.repository; diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/impl/DeviceCodeRepository.java b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/impl/DeviceCodeRepository.java index 011fd24c2a..7b37706002 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/impl/DeviceCodeRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/impl/DeviceCodeRepository.java @@ -18,7 +18,6 @@ package org.mitre.oauth2.repository.impl; import java.util.Collection; -import java.util.Set; import org.mitre.oauth2.model.DeviceCode; diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java index 958dbc1d51..a0067b909a 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java @@ -24,7 +24,6 @@ import org.mitre.oauth2.model.DeviceCode; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; /** * @author jricher @@ -40,7 +39,7 @@ public interface DeviceCodeService { /** * @param dc - * @param o2Auth + * @param o2Auth */ public DeviceCode approveDeviceCode(DeviceCode dc, OAuth2Authentication o2Auth); diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/SystemScopeService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/SystemScopeService.java index d7d1f800ec..b227c4a062 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/SystemScopeService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/SystemScopeService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.service; @@ -37,12 +37,12 @@ public interface SystemScopeService { public static final String RESOURCE_TOKEN_SCOPE = "resource-token"; // this scope manages client-style protected resources public static final String UMA_PROTECTION_SCOPE = "uma_protection"; public static final String UMA_AUTHORIZATION_SCOPE = "uma_authorization"; - - public static final Set reservedScopes = - Sets.newHashSet( - new SystemScope(REGISTRATION_TOKEN_SCOPE), - new SystemScope(RESOURCE_TOKEN_SCOPE) - ); + + public static final Set reservedScopes = + Sets.newHashSet( + new SystemScope(REGISTRATION_TOKEN_SCOPE), + new SystemScope(RESOURCE_TOKEN_SCOPE) + ); public Set getAll(); @@ -56,7 +56,7 @@ public interface SystemScopeService { * Get all the reserved system scopes. These can't be used * by clients directly, but are instead tied to special system * tokens like id tokens and registration access tokens. - * + * * @return */ public Set getReserved(); diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/DefaultClientUserDetailsService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/DefaultClientUserDetailsService.java index f713a80be1..32a7291a32 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/DefaultClientUserDetailsService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/DefaultClientUserDetailsService.java @@ -39,7 +39,7 @@ /** * Shim layer to convert a ClientDetails service into a UserDetails service - * + * * @author AANGANES * */ @@ -53,7 +53,7 @@ public class DefaultClientUserDetailsService implements UserDetailsService { @Autowired private ConfigurationPropertiesBean config; - + @Override public UserDetails loadUserByUsername(String clientId) throws UsernameNotFoundException { @@ -66,7 +66,7 @@ public UserDetails loadUserByUsername(String clientId) throws UsernameNotFoundE if (config.isHeartMode() || // if we're running HEART mode turn off all client secrets (client.getTokenEndpointAuthMethod() != null && - (client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY) || + (client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY) || client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_JWT)))) { // Issue a random password each time to prevent password auth from being used (or skipped) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java index 774f503eb7..c85fa2da63 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java @@ -41,9 +41,9 @@ /** * Loads client details based on URI encoding as passed in from basic auth. - * + * * Should only get called if non-encoded provider fails. - * + * * @author AANGANES * */ @@ -57,7 +57,7 @@ public class UriEncodedClientUserDetailsService implements UserDetailsService { @Autowired private ConfigurationPropertiesBean config; - + @Override public UserDetails loadUserByUsername(String clientId) throws UsernameNotFoundException { diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/ClientDetailsEntityJsonProcessor.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/ClientDetailsEntityJsonProcessor.java index 6957d685b0..f7006e1661 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/ClientDetailsEntityJsonProcessor.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/ClientDetailsEntityJsonProcessor.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect; @@ -95,7 +95,7 @@ /** * Utility class to handle the parsing and serialization of ClientDetails objects. - * + * * @author jricher * */ @@ -104,11 +104,11 @@ public class ClientDetailsEntityJsonProcessor { private static Logger logger = LoggerFactory.getLogger(ClientDetailsEntityJsonProcessor.class); private static JsonParser parser = new JsonParser(); - + /** - * + * * Create an unbound ClientDetailsEntity from the given JSON string. - * + * * @param jsonString * @return the entity if successful, null otherwise */ @@ -205,26 +205,26 @@ public static ClientDetailsEntity parse(JsonElement jsonEl) { c.setRequestUris(getAsStringSet(o, REQUEST_URIS)); c.setClaimsRedirectUris(getAsStringSet(o, CLAIMS_REDIRECT_URIS)); - + c.setCodeChallengeMethod(getAsPkceAlgorithm(o, CODE_CHALLENGE_METHOD)); - + c.setSoftwareId(getAsString(o, SOFTWARE_ID)); c.setSoftwareVersion(getAsString(o, SOFTWARE_VERSION)); - + // note that this does not process or validate the software statement, that's handled in other components String softwareStatement = getAsString(o, SOFTWARE_STATEMENT); if (!Strings.isNullOrEmpty(softwareStatement)) { try { - JWT softwareStatementJwt = JWTParser.parse(softwareStatement); - c.setSoftwareStatement(softwareStatementJwt); + JWT softwareStatementJwt = JWTParser.parse(softwareStatement); + c.setSoftwareStatement(softwareStatementJwt); } catch (ParseException e) { logger.warn("Error parsing software statement", e); return null; } } - - - + + + return c; } else { return null; @@ -345,18 +345,18 @@ public static JsonObject serialize(RegisteredClient c) { o.addProperty(INITIATE_LOGIN_URI, c.getInitiateLoginUri()); o.add(POST_LOGOUT_REDIRECT_URIS, getAsArray(c.getPostLogoutRedirectUris())); o.add(REQUEST_URIS, getAsArray(c.getRequestUris())); - + o.add(CLAIMS_REDIRECT_URIS, getAsArray(c.getClaimsRedirectUris())); - + o.addProperty(CODE_CHALLENGE_METHOD, c.getCodeChallengeMethod() != null ? c.getCodeChallengeMethod().getName() : null); - + o.addProperty(SOFTWARE_ID, c.getSoftwareId()); o.addProperty(SOFTWARE_VERSION, c.getSoftwareVersion()); - + if (c.getSoftwareStatement() != null) { o.addProperty(SOFTWARE_STATEMENT, c.getSoftwareStatement().serialize()); } - + return o; } diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ConfigurationBeanLocaleResolver.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ConfigurationBeanLocaleResolver.java index 2cf1b45b5c..d79c8d0f1f 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ConfigurationBeanLocaleResolver.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ConfigurationBeanLocaleResolver.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.config; @@ -28,13 +28,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.i18n.LocaleContext; import org.springframework.context.i18n.TimeZoneAwareLocaleContext; -import org.springframework.stereotype.Component; import org.springframework.web.servlet.i18n.AbstractLocaleContextResolver; /** - * + * * Resolve the server's locale from the injected ConfigurationPropertiesBean. - * + * * @author jricher * */ diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ConfigurationPropertiesBean.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ConfigurationPropertiesBean.java index ddae448a2a..e68938a8c1 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ConfigurationPropertiesBean.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ConfigurationPropertiesBean.java @@ -35,7 +35,7 @@ * Bean to hold configuration information that must be injected into various parts * of our application. Set all of the properties here, and autowire a reference * to this bean if you need access to any configuration properties. - * + * * @author AANGANES * */ @@ -49,7 +49,7 @@ public class ConfigurationPropertiesBean { private String issuer; private String topbarTitle; - + private String shortTopbarTitle; private String logoImageUrl; @@ -61,14 +61,14 @@ public class ConfigurationPropertiesBean { private boolean forceHttps = false; // by default we just log a warning for HTTPS deployment private Locale locale = Locale.ENGLISH; // we default to the english translation - + private List languageNamespaces = Lists.newArrayList("messages"); - private boolean dualClient = false; - - private boolean heartMode = false; + private boolean dualClient = false; - public ConfigurationPropertiesBean() { + private boolean heartMode = false; + + public ConfigurationPropertiesBean() { } @@ -87,7 +87,7 @@ public void checkConfigConsistency() { logger.warn("\n\n**\n** WARNING: Configured issuer url is not using https scheme.\n**\n\n"); } } - + if (languageNamespaces == null || languageNamespaces.isEmpty()) { logger.error("No configured language namespaces! Text rendering will fail!"); } @@ -120,14 +120,14 @@ public String getTopbarTitle() { public void setTopbarTitle(String topbarTitle) { this.topbarTitle = topbarTitle; } - + /** - * @return If shortTopbarTitle is undefined, returns topbarTitle. + * @return If shortTopbarTitle is undefined, returns topbarTitle. */ public String getShortTopbarTitle() { return shortTopbarTitle == null ? topbarTitle : shortTopbarTitle; } - + public void setShortTopbarTitle(String shortTopbarTitle) { this.shortTopbarTitle = shortTopbarTitle; } @@ -211,37 +211,37 @@ public void setLanguageNamespaces(List languageNamespaces) { } /** - * @return true if dual client is configured, otherwise false - */ - public boolean isDualClient() { - if (isHeartMode()) { - return false; // HEART mode is incompatible with dual client mode - } else { - return dualClient; - } - } - - /** - * @param dualClient the dual client configuration - */ - public void setDualClient(boolean dualClient) { - this.dualClient = dualClient; - } - - /** - * Get the list of namespaces as a JSON string, for injection into the JavaScript UI - * @return - */ - public String getLanguageNamespacesString() { - return new Gson().toJson(getLanguageNamespaces()); - } - - /** - * Get the default namespace (first in the nonempty list) - */ - public String getDefaultLanguageNamespace() { - return getLanguageNamespaces().get(0); - } + * @return true if dual client is configured, otherwise false + */ + public boolean isDualClient() { + if (isHeartMode()) { + return false; // HEART mode is incompatible with dual client mode + } else { + return dualClient; + } + } + + /** + * @param dualClient the dual client configuration + */ + public void setDualClient(boolean dualClient) { + this.dualClient = dualClient; + } + + /** + * Get the list of namespaces as a JSON string, for injection into the JavaScript UI + * @return + */ + public String getLanguageNamespacesString() { + return new Gson().toJson(getLanguageNamespaces()); + } + + /** + * Get the default namespace (first in the nonempty list) + */ + public String getDefaultLanguageNamespace() { + return getLanguageNamespaces().get(0); + } /** * @return the heartMode diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/JWKSetEditor.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/JWKSetEditor.java index c9fb8988c0..e02ea4fa74 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/JWKSetEditor.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/JWKSetEditor.java @@ -25,7 +25,7 @@ /** * Allows JWK Set strings to be used in XML configurations. - * + * * @author jricher * */ diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ServerConfiguration.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ServerConfiguration.java index b80f1633b8..39d53f0bea 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ServerConfiguration.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ServerConfiguration.java @@ -25,16 +25,16 @@ /** - * + * * Container class for a client's view of a server's configuration - * + * * @author nemonik, jricher - * + * */ public class ServerConfiguration { /* - * + * issuer REQUIRED. URL using the https scheme with no query or fragment component that the OP asserts as its Issuer Identifier. authorization_endpoint diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/UIConfiguration.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/UIConfiguration.java index 7bc689d847..7890b6964e 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/UIConfiguration.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/UIConfiguration.java @@ -20,9 +20,9 @@ import java.util.Set; /** - * + * * Bean for UI (front-end) configuration to be read at start-up. - * + * * @author jricher * */ @@ -30,7 +30,7 @@ public class UIConfiguration { private Set jsFiles; private Set templateFiles; - + /** * @return the jsFiles */ @@ -55,5 +55,5 @@ public Set getTemplateFiles() { public void setTemplateFiles(Set templateFiles) { this.templateFiles = templateFiles; } - + } diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/Address.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/Address.java index 59e6866715..be0ea60798 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/Address.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/Address.java @@ -25,62 +25,62 @@ public interface Address extends Serializable { * @return */ public Long getId(); - + /** * @return the formatted address */ public String getFormatted(); - + /** * @param formatted the formatted address to set */ public void setFormatted(String formatted); - + /** * @return the streetAddress */ public String getStreetAddress(); - + /** * @param streetAddress the streetAddress to set */ public void setStreetAddress(String streetAddress); - + /** * @return the locality */ public String getLocality(); - + /** * @param locality the locality to set */ public void setLocality(String locality); - + /** * @return the region */ public String getRegion(); - + /** * @param region the region to set */ public void setRegion(String region); - + /** * @return the postalCode */ public String getPostalCode(); - + /** * @param postalCode the postalCode to set */ public void setPostalCode(String postalCode); - + /** * @return the country */ public String getCountry(); - + /** * @param country the country to set */ diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ApprovedSite.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ApprovedSite.java index 6be0bb8f41..e4f8e5c8d8 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ApprovedSite.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ApprovedSite.java @@ -20,7 +20,6 @@ import java.util.Set; import javax.persistence.Basic; -import javax.persistence.CascadeType; import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.ElementCollection; @@ -32,15 +31,10 @@ import javax.persistence.JoinColumn; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; -import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.Transient; -import org.mitre.oauth2.model.OAuth2AccessTokenEntity; - -import com.google.common.collect.Sets; - @Entity @Table(name="approved_site") @NamedQueries({ diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/BlacklistedSite.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/BlacklistedSite.java index 0447151ec6..f6c1a2891f 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/BlacklistedSite.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/BlacklistedSite.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.model; diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/CachedImage.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/CachedImage.java index 7aa18d3ca4..a29732221f 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/CachedImage.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/CachedImage.java @@ -22,11 +22,11 @@ * */ public class CachedImage { - + private byte[] data; private String contentType; private long length; - + /** * @return the data */ diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ClientStat.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ClientStat.java index 585df3b2b5..b0289c98d6 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ClientStat.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/ClientStat.java @@ -38,5 +38,5 @@ public Integer getApprovedSiteCount() { public void setApprovedSiteCount(Integer count) { this.approvedSiteCount = count; } - + } diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultAddress.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultAddress.java index 237d1e68ab..3093e1f6e7 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultAddress.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultAddress.java @@ -16,8 +16,6 @@ *******************************************************************************/ package org.mitre.openid.connect.model; -import java.io.Serializable; - import javax.persistence.Basic; import javax.persistence.Column; import javax.persistence.Entity; @@ -63,6 +61,7 @@ public DefaultAddress(Address address) { /** * @return the formatted address string */ + @Override @Basic @Column(name = "formatted") public String getFormatted() { @@ -71,12 +70,14 @@ public String getFormatted() { /** * @param formatted the formatted address to set */ + @Override public void setFormatted(String formatted) { this.formatted = formatted; } /** * @return the streetAddress */ + @Override @Basic @Column(name="street_address") public String getStreetAddress() { @@ -85,12 +86,14 @@ public String getStreetAddress() { /** * @param streetAddress the streetAddress to set */ + @Override public void setStreetAddress(String streetAddress) { this.streetAddress = streetAddress; } /** * @return the locality */ + @Override @Basic @Column(name = "locality") public String getLocality() { @@ -99,12 +102,14 @@ public String getLocality() { /** * @param locality the locality to set */ + @Override public void setLocality(String locality) { this.locality = locality; } /** * @return the region */ + @Override @Basic @Column(name = "region") public String getRegion() { @@ -113,12 +118,14 @@ public String getRegion() { /** * @param region the region to set */ + @Override public void setRegion(String region) { this.region = region; } /** * @return the postalCode */ + @Override @Basic @Column(name="postal_code") public String getPostalCode() { @@ -127,12 +134,14 @@ public String getPostalCode() { /** * @param postalCode the postalCode to set */ + @Override public void setPostalCode(String postalCode) { this.postalCode = postalCode; } /** * @return the country */ + @Override @Basic @Column(name = "country") public String getCountry() { @@ -141,6 +150,7 @@ public String getCountry() { /** * @param country the country to set */ + @Override public void setCountry(String country) { this.country = country; } @@ -148,6 +158,7 @@ public void setCountry(String country) { /** * @return the id */ + @Override @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name = "id") diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java index 97c9d57ddd..5fe70b5c07 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java @@ -379,7 +379,7 @@ public Address getAddress() { */ @Override public void setAddress(Address address) { - if (address != null) { + if (address != null) { this.address = new DefaultAddress(address); } else { this.address = null; @@ -499,7 +499,7 @@ public static UserInfo fromJson(JsonObject obj) { ui.setPhoneNumber(nullSafeGetString(obj, "phone_number")); ui.setPhoneNumberVerified(obj.has("phone_number_verified") && obj.get("phone_number_verified").isJsonPrimitive() ? obj.get("phone_number_verified").getAsBoolean() : null); - + if (obj.has("address") && obj.get("address").isJsonObject()) { JsonObject addr = obj.get("address").getAsJsonObject(); ui.setAddress(new DefaultAddress()); diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/OIDCAuthenticationToken.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/OIDCAuthenticationToken.java index 85be3aad6b..ca4e996b04 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/OIDCAuthenticationToken.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/OIDCAuthenticationToken.java @@ -30,9 +30,9 @@ import com.nimbusds.jwt.JWTParser; /** - * + * * @author Michael Walsh, Justin Richer - * + * */ public class OIDCAuthenticationToken extends AbstractAuthenticationToken { @@ -49,9 +49,9 @@ public class OIDCAuthenticationToken extends AbstractAuthenticationToken { /** * Constructs OIDCAuthenticationToken with a full set of authorities, marking this as authenticated. - * + * * Set to authenticated. - * + * * Constructs a Principal out of the subject and issuer. * @param subject * @param authorities @@ -78,7 +78,7 @@ public OIDCAuthenticationToken(String subject, String issuer, /* * (non-Javadoc) - * + * * @see org.springframework.security.core.Authentication#getCredentials() */ @Override diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/PairwiseIdentifier.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/PairwiseIdentifier.java index c46f1b1e10..e651d8b138 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/PairwiseIdentifier.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/PairwiseIdentifier.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.model; @@ -30,9 +30,9 @@ import javax.persistence.Table; /** - * + * * Holds the generated pairwise identifiers for a user. Can be tied to either a client ID or a sector identifier URL. - * + * * @author jricher * */ diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/PendingOIDCAuthenticationToken.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/PendingOIDCAuthenticationToken.java index 70aed558c5..fbcd9d52e1 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/PendingOIDCAuthenticationToken.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/PendingOIDCAuthenticationToken.java @@ -33,7 +33,7 @@ /** * AuthenticationToken for use as a data shuttle from the filter to the auth provider. - * + * * @author jricher * */ @@ -52,9 +52,9 @@ public class PendingOIDCAuthenticationToken extends AbstractAuthenticationToken /** * Constructs OIDCAuthenticationToken for use as a data shuttle from the filter to the auth provider. - * + * * Set to not-authenticated. - * + * * Constructs a Principal out of the subject and issuer. * @param sub * @param idToken @@ -80,7 +80,7 @@ public PendingOIDCAuthenticationToken (String subject, String issuer, /* * (non-Javadoc) - * + * * @see org.springframework.security.core.Authentication#getCredentials() */ @Override diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/UserInfo.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/UserInfo.java index 543486a2ce..5029290440 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/UserInfo.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/UserInfo.java @@ -184,12 +184,12 @@ public interface UserInfo extends Serializable { public void setPhoneNumber(String phoneNumber); /** - * + * */ public Boolean getPhoneNumberVerified(); /** - * + * * @param phoneNumberVerified */ public void setPhoneNumberVerified(Boolean phoneNumberVerified); @@ -216,20 +216,20 @@ public interface UserInfo extends Serializable { /** - * + * * @return */ public String getBirthdate(); /** - * + * * @param birthdate */ public void setBirthdate(String birthdate); /** * Serialize this UserInfo object to JSON. - * + * * @return */ public JsonObject toJson(); diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/AddressRepository.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/AddressRepository.java index 1730093133..84352e4d88 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/AddressRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/AddressRepository.java @@ -28,7 +28,7 @@ public interface AddressRepository { /** * Returns the Address for the given id - * + * * @param id * id the id of the Address * @return a valid Address if it exists, null otherwise diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/ApprovedSiteRepository.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/ApprovedSiteRepository.java index 1919254b14..502fd1b22a 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/ApprovedSiteRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/ApprovedSiteRepository.java @@ -30,7 +30,7 @@ public interface ApprovedSiteRepository { /** * Returns the ApprovedSite for the given id - * + * * @param id * id the id of the ApprovedSite * @return a valid ApprovedSite if it exists, null otherwise @@ -39,7 +39,7 @@ public interface ApprovedSiteRepository { /** * Return a collection of all ApprovedSites managed by this repository - * + * * @return the ApprovedSite collection, or null */ public Collection getAll(); @@ -47,7 +47,7 @@ public interface ApprovedSiteRepository { /** * Return a collection of ApprovedSite managed by this repository matching the * provided client ID and user ID - * + * * @param clientId * @param userId * @return @@ -56,7 +56,7 @@ public interface ApprovedSiteRepository { /** * Removes the given ApprovedSite from the repository - * + * * @param aggregator * the ApprovedSite object to remove */ @@ -64,7 +64,7 @@ public interface ApprovedSiteRepository { /** * Persists an ApprovedSite - * + * * @param aggregator * valid ApprovedSite instance * @return the persisted entity diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/BlacklistedSiteRepository.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/BlacklistedSiteRepository.java index cab432ac68..2e98faf982 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/BlacklistedSiteRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/BlacklistedSiteRepository.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.repository; diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/PairwiseIdentifierRepository.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/PairwiseIdentifierRepository.java index 1a168d7a1d..4bf1ea3130 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/PairwiseIdentifierRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/PairwiseIdentifierRepository.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.repository; @@ -29,7 +29,7 @@ public interface PairwiseIdentifierRepository { /** * Get a pairwise identifier by its associated user subject and sector identifier. - * + * * @param sub * @param sectorIdentifierUri * @return @@ -38,7 +38,7 @@ public interface PairwiseIdentifierRepository { /** * Save a pairwise identifier to the database. - * + * * @param pairwise */ public void save(PairwiseIdentifier pairwise); diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/UserInfoRepository.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/UserInfoRepository.java index 9ea3011d53..99dcb5570b 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/UserInfoRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/UserInfoRepository.java @@ -20,7 +20,7 @@ /** * UserInfo repository interface - * + * * @author Michael Joseph Walsh * */ @@ -34,9 +34,9 @@ public interface UserInfoRepository { public UserInfo getByUsername(String username); /** - * + * * Get the UserInfo object by its email field - * + * * @param email * @return */ diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/WhitelistedSiteRepository.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/WhitelistedSiteRepository.java index 1e6f91028d..3452078563 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/WhitelistedSiteRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/repository/WhitelistedSiteRepository.java @@ -22,22 +22,22 @@ /** * WhitelistedSite repository interface - * + * * @author Michael Joseph Walsh, aanganes - * + * */ public interface WhitelistedSiteRepository { /** * Return a collection of all WhitelistedSite managed by this repository - * + * * @return the WhitelistedSite collection, or null */ public Collection getAll(); /** * Returns the WhitelistedSite for the given id - * + * * @param id * id the id of the WhitelistedSite * @return a valid WhitelistedSite if it exists, null otherwise @@ -46,7 +46,7 @@ public interface WhitelistedSiteRepository { /** * Find a WhitelistedSite by its associated ClientDetails reference - * + * * @param client the Relying Party * @return the corresponding WhitelistedSite if one exists for the RP, or null */ @@ -54,7 +54,7 @@ public interface WhitelistedSiteRepository { /** * Return a collection of the WhitelistedSites created by a given user - * + * * @param creator the id of the admin who may have created some WhitelistedSites * @return the collection of corresponding WhitelistedSites, if any, or null */ @@ -62,7 +62,7 @@ public interface WhitelistedSiteRepository { /** * Removes the given IdToken from the repository - * + * * @param whitelistedSite * the WhitelistedSite object to remove */ @@ -70,7 +70,7 @@ public interface WhitelistedSiteRepository { /** * Persists a WhitelistedSite - * + * * @param whitelistedSite * @return */ diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java index 23b5519b92..bfc1f71224 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ApprovedSiteService.java @@ -27,9 +27,9 @@ /** * Interface for ApprovedSite service - * + * * @author Michael Joseph Walsh, aanganes - * + * */ public interface ApprovedSiteService { @@ -38,7 +38,7 @@ public interface ApprovedSiteService { /** * Return a collection of all ApprovedSites - * + * * @return the ApprovedSite collection, or null */ public Collection getAll(); @@ -46,7 +46,7 @@ public interface ApprovedSiteService { /** * Return a collection of ApprovedSite managed by this repository matching the * provided client ID and user ID - * + * * @param clientId * @param userId * @return @@ -55,7 +55,7 @@ public interface ApprovedSiteService { /** * Save an ApprovedSite - * + * * @param approvedSite * the ApprovedSite to be saved */ @@ -63,7 +63,7 @@ public interface ApprovedSiteService { /** * Get ApprovedSite for id - * + * * @param id * id for ApprovedSite * @return ApprovedSite for id, or null @@ -72,7 +72,7 @@ public interface ApprovedSiteService { /** * Remove the ApprovedSite - * + * * @param approvedSite * the ApprovedSite to remove */ @@ -109,5 +109,5 @@ public interface ApprovedSiteService { * @return */ public List getApprovedAccessTokens(ApprovedSite approvedSite); - + } diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/BlacklistedSiteService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/BlacklistedSiteService.java index 8bb09fc175..0b2724d701 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/BlacklistedSiteService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/BlacklistedSiteService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.service; diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataService.java index ec07f00359..c5f56b0589 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataService.java @@ -47,7 +47,7 @@ public interface MITREidDataService { /** * Write out the current server state to the given JSON writer as a JSON object - * + * * @param writer * @throws IOException */ @@ -55,15 +55,15 @@ public interface MITREidDataService { /** * Read in the current server state from the given JSON reader as a JSON object - * + * * @param reader */ void importData(JsonReader reader) throws IOException; /** - * Return true if the this data service supports the given version. This is called before + * Return true if the this data service supports the given version. This is called before * handing the service the reader through its importData function. - * + * * @param version * @return */ diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceExtension.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceExtension.java index 98ddd027f4..cdd9e1d6e2 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceExtension.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceExtension.java @@ -23,19 +23,19 @@ import com.google.gson.stream.JsonWriter; /** - * A modular extension to the data import/export layer. Any instances of this need to be + * A modular extension to the data import/export layer. Any instances of this need to be * declared as beans to be picked up by the data services. - * + * * @author jricher * */ public interface MITREidDataServiceExtension { /** - * Export any data for this extension. This is called from the top level object. + * Export any data for this extension. This is called from the top level object. * All extensions MUST return the writer to a state such that another member of * the top level object can be written next. - * + * * @param writer */ void exportExtensionData(JsonWriter writer) throws IOException; @@ -46,20 +46,20 @@ public interface MITREidDataServiceExtension { * such that another member of the top level object can be read next. The name of * the data element being imported is passed in as name. If the extension does not * support this data element, it must return without advancing the reader. - * + * * Returns "true" if the item was processed, "false" otherwise. - * + * * @param reader */ boolean importExtensionData(String name, JsonReader reader) throws IOException; /** - * Signal the extension to wrap up all object processing and finalize its + * Signal the extension to wrap up all object processing and finalize its */ void fixExtensionObjectReferences(MITREidDataServiceMaps maps); /** - * Return + * Return * @param mitreidConnect13 * @return */ diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceMaps.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceMaps.java index 27430c3b24..5ea918f169 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceMaps.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/MITREidDataServiceMaps.java @@ -115,5 +115,5 @@ public void clearAll() { accessTokenOldToNewIdMap.clear(); grantOldToNewIdMap.clear(); } - + } diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/OIDCTokenService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/OIDCTokenService.java index 1b79805a2f..4c3ac5a36b 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/OIDCTokenService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/OIDCTokenService.java @@ -26,7 +26,7 @@ /** * Service to create specialty OpenID Connect tokens. - * + * * @author Amanda Anganes * */ @@ -34,7 +34,7 @@ public interface OIDCTokenService { /** * Create an id token with the information provided. - * + * * @param client * @param request * @param issueTime @@ -49,7 +49,7 @@ public JWT createIdToken( /** * Create a registration access token for the given client. - * + * * @param client * @return */ @@ -57,7 +57,7 @@ public JWT createIdToken( /** * Create a resource access token for the given client (protected resource). - * + * * @param client * @return */ diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/PairwiseIdentiferService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/PairwiseIdentiferService.java index 41446759a9..7b524ad7b9 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/PairwiseIdentiferService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/PairwiseIdentiferService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.service; @@ -30,9 +30,9 @@ public interface PairwiseIdentiferService { /** * Calcualtes the pairwise identifier for the given userinfo object and client. - * + * * Returns 'null' if no identifer could be calculated. - * + * * @param userInfo * @param client * @return diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ScopeClaimTranslationService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ScopeClaimTranslationService.java index d5bb4ec44c..de4886203e 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ScopeClaimTranslationService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/ScopeClaimTranslationService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.service; diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/StatsService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/StatsService.java index 502094b603..b129927529 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/StatsService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/StatsService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.service; @@ -34,14 +34,14 @@ public interface StatsService { * approvalCount: total approved sites * userCount: unique users * clientCount: unique clients - * + * * @return */ public Map getSummaryStats(); /** * Calculate the usage count for a single client - * + * * @param clientId the id of the client to search on * @return */ diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/UserInfoService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/UserInfoService.java index 5b3161b1a2..e042084027 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/UserInfoService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/UserInfoService.java @@ -20,9 +20,9 @@ /** * Interface for UserInfo service - * + * * @author Michael Joseph Walsh - * + * */ public interface UserInfoService { @@ -46,7 +46,7 @@ public interface UserInfoService { /** * Get the user registered at this server with the given email address. - * + * * @param email * @return */ diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/WhitelistedSiteService.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/WhitelistedSiteService.java index 4d805b7ac9..570f899225 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/service/WhitelistedSiteService.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/service/WhitelistedSiteService.java @@ -22,22 +22,22 @@ /** * Interface for WhitelistedSite service - * + * * @author Michael Joseph Walsh, aanganes - * + * */ public interface WhitelistedSiteService { /** * Return a collection of all WhitelistedSite managed by this service - * + * * @return the WhitelistedSite collection, or null */ public Collection getAll(); /** * Returns the WhitelistedSite for the given id - * + * * @param id * id the id of the WhitelistedSite * @return a valid WhitelistedSite if it exists, null otherwise @@ -46,7 +46,7 @@ public interface WhitelistedSiteService { /** * Find a WhitelistedSite by its associated ClientDetails reference - * + * * @param client the Relying Party * @return the corresponding WhitelistedSite if one exists for the RP, or null */ @@ -56,7 +56,7 @@ public interface WhitelistedSiteService { /** * Removes the given WhitelistedSite from the repository - * + * * @param address * the WhitelistedSite object to remove */ @@ -64,7 +64,7 @@ public interface WhitelistedSiteService { /** * Persists a new WhitelistedSite - * + * * @param whitelistedSite * the WhitelistedSite to be saved * @return diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/view/JWKSetView.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/view/JWKSetView.java index 4398a07f02..801802d5dd 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/view/JWKSetView.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/view/JWKSetView.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.view; diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/web/UserInfoInterceptor.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/web/UserInfoInterceptor.java index b9f88f5066..71fcacd284 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/web/UserInfoInterceptor.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/web/UserInfoInterceptor.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.web; @@ -44,24 +44,24 @@ /** * Injects the UserInfo object for the current user into the current model's context, if both exist. Allows JSPs and the like to call "userInfo.name" and other fields. - * + * * @author jricher * */ public class UserInfoInterceptor extends HandlerInterceptorAdapter { private Gson gson = new GsonBuilder() - .registerTypeHierarchyAdapter(GrantedAuthority.class, new JsonSerializer() { - @Override - public JsonElement serialize(GrantedAuthority src, Type typeOfSrc, JsonSerializationContext context) { - return new JsonPrimitive(src.getAuthority()); - } - }) - .create(); + .registerTypeHierarchyAdapter(GrantedAuthority.class, new JsonSerializer() { + @Override + public JsonElement serialize(GrantedAuthority src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.getAuthority()); + } + }) + .create(); @Autowired (required = false) private UserInfoService userInfoService; - + private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl(); @Override @@ -72,7 +72,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons if (auth instanceof Authentication){ request.setAttribute("userAuthorities", gson.toJson(auth.getAuthorities())); } - + if (!trustResolver.isAnonymous(auth)) { // skip lookup on anonymous logins if (auth instanceof OIDCAuthenticationToken) { // if they're logging into this server from a remote OIDC server, pass through their user info @@ -87,10 +87,10 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons } else { // don't bother checking if we don't have a principal or a userInfoService to work with if (auth != null && auth.getName() != null && userInfoService != null) { - + // try to look up a user based on the principal's name UserInfo user = userInfoService.getByUsername(auth.getName()); - + // if we have one, inject it so views can use it if (user != null) { request.setAttribute("userInfo", user); diff --git a/openid-connect-common/src/main/java/org/mitre/uma/model/Claim.java b/openid-connect-common/src/main/java/org/mitre/uma/model/Claim.java index 4b735e866e..d8f9bde9cb 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/model/Claim.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/model/Claim.java @@ -118,9 +118,9 @@ public void setClaimType(String claimType) { @ElementCollection(fetch = FetchType.EAGER) @Column(name = "claim_token_format") @CollectionTable( - name = "claim_token_format", - joinColumns = @JoinColumn(name = "owner_id") - ) + name = "claim_token_format", + joinColumns = @JoinColumn(name = "owner_id") + ) public Set getClaimTokenFormat() { return claimTokenFormat; } @@ -137,9 +137,9 @@ public void setClaimTokenFormat(Set claimTokenFormat) { @ElementCollection(fetch = FetchType.EAGER) @Column(name = "issuer") @CollectionTable( - name = "claim_issuer", - joinColumns = @JoinColumn(name = "owner_id") - ) + name = "claim_issuer", + joinColumns = @JoinColumn(name = "owner_id") + ) public Set getIssuer() { return issuer; } diff --git a/openid-connect-common/src/main/java/org/mitre/uma/model/ClaimProcessingResult.java b/openid-connect-common/src/main/java/org/mitre/uma/model/ClaimProcessingResult.java index f6b00cf228..962918eeb4 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/model/ClaimProcessingResult.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/model/ClaimProcessingResult.java @@ -21,7 +21,7 @@ /** * Data shuttle to return results of the claims processing service. - * + * * @author jricher * */ diff --git a/openid-connect-common/src/main/java/org/mitre/uma/model/Permission.java b/openid-connect-common/src/main/java/org/mitre/uma/model/Permission.java index d856ef8477..5a8ff06e21 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/model/Permission.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/model/Permission.java @@ -81,9 +81,9 @@ public void setResourceSet(ResourceSet resourceSet) { @ElementCollection(fetch = FetchType.EAGER) @Column(name = "scope") @CollectionTable( - name = "permission_scope", - joinColumns = @JoinColumn(name = "owner_id") - ) + name = "permission_scope", + joinColumns = @JoinColumn(name = "owner_id") + ) public Set getScopes() { return scopes; } diff --git a/openid-connect-common/src/main/java/org/mitre/uma/model/PermissionTicket.java b/openid-connect-common/src/main/java/org/mitre/uma/model/PermissionTicket.java index ce02deecd4..70097572ca 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/model/PermissionTicket.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/model/PermissionTicket.java @@ -39,9 +39,9 @@ import javax.persistence.TemporalType; /** - * + * * An UMA permission, used in the protection API. - * + * * @author jricher * */ @@ -141,7 +141,7 @@ public void setExpiration(Date expiration) { name = "claim_to_permission_ticket", joinColumns = @JoinColumn(name = "permission_ticket_id"), inverseJoinColumns = @JoinColumn(name = "claim_id") - ) + ) public Collection getClaimsSupplied() { return claimsSupplied; } diff --git a/openid-connect-common/src/main/java/org/mitre/uma/model/Policy.java b/openid-connect-common/src/main/java/org/mitre/uma/model/Policy.java index c29a05208c..14714a4ed7 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/model/Policy.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/model/Policy.java @@ -37,7 +37,7 @@ /** * A set of claims required to fulfill a given permission. - * + * * @author jricher * */ @@ -109,9 +109,9 @@ public void setClaimsRequired(Collection claimsRequired) { @ElementCollection(fetch = FetchType.EAGER) @Column(name = "scope") @CollectionTable( - name = "policy_scope", - joinColumns = @JoinColumn(name = "owner_id") - ) + name = "policy_scope", + joinColumns = @JoinColumn(name = "owner_id") + ) public Set getScopes() { return scopes; } diff --git a/openid-connect-common/src/main/java/org/mitre/uma/model/ResourceSet.java b/openid-connect-common/src/main/java/org/mitre/uma/model/ResourceSet.java index 8922ad91f6..6ea39176f6 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/model/ResourceSet.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/model/ResourceSet.java @@ -137,9 +137,9 @@ public void setType(String type) { @ElementCollection(fetch = FetchType.EAGER) @Column(name = "scope") @CollectionTable( - name = "resource_set_scope", - joinColumns = @JoinColumn(name = "owner_id") - ) + name = "resource_set_scope", + joinColumns = @JoinColumn(name = "owner_id") + ) public Set getScopes() { return scopes; } diff --git a/openid-connect-common/src/main/java/org/mitre/uma/model/SavedRegisteredClient.java b/openid-connect-common/src/main/java/org/mitre/uma/model/SavedRegisteredClient.java index c65aa8d851..03ae277e4a 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/model/SavedRegisteredClient.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/model/SavedRegisteredClient.java @@ -52,7 +52,7 @@ public Long getId() { } /** - * + * * @param id the id to set */ public void setId(Long id) { diff --git a/openid-connect-common/src/main/java/org/mitre/uma/repository/PermissionRepository.java b/openid-connect-common/src/main/java/org/mitre/uma/repository/PermissionRepository.java index 816e70866f..fc46086134 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/repository/PermissionRepository.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/repository/PermissionRepository.java @@ -30,9 +30,9 @@ public interface PermissionRepository { /** - * + * * Save a permission ticket. - * + * * @param p * @return */ @@ -40,7 +40,7 @@ public interface PermissionRepository { /** * Get the permission indicated by its ticket value. - * + * * @param ticket * @return */ @@ -48,14 +48,14 @@ public interface PermissionRepository { /** * Get all the tickets in the system (used by the import/export API) - * + * * @return */ public Collection getAll(); /** * Save a permission object with no associated ticket (used by the import/export API) - * + * * @param p * @return */ @@ -63,7 +63,7 @@ public interface PermissionRepository { /** * Get a permission object by its ID (used by the import/export API) - * + * * @param permissionId * @return */ @@ -71,7 +71,7 @@ public interface PermissionRepository { /** * Get all permission tickets issued against a resource set (called when RS is deleted) - * + * * @param rs * @return */ @@ -79,7 +79,7 @@ public interface PermissionRepository { /** * Remove the specified ticket. - * + * * @param ticket */ public void remove(PermissionTicket ticket); diff --git a/openid-connect-common/src/main/java/org/mitre/uma/service/ClaimsProcessingService.java b/openid-connect-common/src/main/java/org/mitre/uma/service/ClaimsProcessingService.java index 202a3f8805..f863a577ff 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/service/ClaimsProcessingService.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/service/ClaimsProcessingService.java @@ -22,20 +22,20 @@ import org.mitre.uma.model.ResourceSet; /** - * + * * Processes claims presented during an UMA transaction. - * + * * @author jricher * */ public interface ClaimsProcessingService { /** - * + * * Determine whether or not the claims that have been supplied are * sufficient to fulfill the requirements given by the claims that * are required. - * + * * @param rs the required claims to check against * @param ticket the supplied claims to test * @return the result of the claims processing action diff --git a/openid-connect-common/src/main/java/org/mitre/uma/service/PermissionService.java b/openid-connect-common/src/main/java/org/mitre/uma/service/PermissionService.java index 1f6e547d6b..1c76fc99c2 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/service/PermissionService.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/service/PermissionService.java @@ -39,9 +39,9 @@ public interface PermissionService { public PermissionTicket createTicket(ResourceSet resourceSet, Set scopes); /** - * + * * Read the permission associated with the given ticket. - * + * * @param the ticket value to search on * @return the permission object, or null if none is found */ @@ -49,7 +49,7 @@ public interface PermissionService { /** * Save the updated permission ticket to the database. Does not create a new ticket. - * + * * @param ticket * @return */ diff --git a/openid-connect-common/src/main/java/org/mitre/uma/service/ResourceSetService.java b/openid-connect-common/src/main/java/org/mitre/uma/service/ResourceSetService.java index 5d990faac5..22b2f0f8a3 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/service/ResourceSetService.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/service/ResourceSetService.java @@ -22,9 +22,9 @@ import org.mitre.uma.model.ResourceSet; /** - * + * * Manage registered resource sets at this authorization server. - * + * * @author jricher * */ diff --git a/openid-connect-common/src/main/java/org/mitre/uma/service/SavedRegisteredClientService.java b/openid-connect-common/src/main/java/org/mitre/uma/service/SavedRegisteredClientService.java index ee3e5329a7..eab15c01fa 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/service/SavedRegisteredClientService.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/service/SavedRegisteredClientService.java @@ -27,10 +27,10 @@ * */ public interface SavedRegisteredClientService { - + /** * Get a list of all the registered clients that we know about. - * + * * @return */ Collection getAll(); diff --git a/openid-connect-common/src/main/java/org/mitre/uma/service/UmaTokenService.java b/openid-connect-common/src/main/java/org/mitre/uma/service/UmaTokenService.java index 96b1277b5e..ccea01368f 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/service/UmaTokenService.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/service/UmaTokenService.java @@ -24,7 +24,7 @@ /** * Service to create special tokens for UMA. - * + * * @author jricher * */ @@ -32,7 +32,7 @@ public interface UmaTokenService { /** * Create the RPT from the given authentication and ticket. - * + * */ public OAuth2AccessTokenEntity createRequestingPartyToken(OAuth2Authentication o2auth, PermissionTicket ticket, Policy policy); diff --git a/openid-connect-common/src/main/java/org/mitre/util/JsonUtils.java b/openid-connect-common/src/main/java/org/mitre/util/JsonUtils.java index dc2c53f951..50cae7060f 100644 --- a/openid-connect-common/src/main/java/org/mitre/util/JsonUtils.java +++ b/openid-connect-common/src/main/java/org/mitre/util/JsonUtils.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.util; @@ -48,7 +48,7 @@ /** * A collection of null-safe converters from common classes and JSON elements, using GSON. - * + * * @author jricher * */ @@ -86,7 +86,7 @@ public static JsonElement getAsArray(Set value, boolean preserveEmpty) { return gson.toJsonTree(value, new TypeToken>(){}.getType()); } } - + /** * Gets the value of the given member (expressed as integer seconds since epoch) as a Date */ @@ -287,19 +287,19 @@ public static Map readMap(JsonReader reader) throws IOException { String name = reader.nextName(); Object value = null; switch(reader.peek()) { - case STRING: - value = reader.nextString(); - break; - case BOOLEAN: - value = reader.nextBoolean(); - break; - case NUMBER: - value = reader.nextLong(); - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + case STRING: + value = reader.nextString(); + break; + case BOOLEAN: + value = reader.nextBoolean(); + break; + case NUMBER: + value = reader.nextLong(); + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; } map.put(name, value); } @@ -311,21 +311,21 @@ public static Set readSet(JsonReader reader) throws IOException { Set arraySet = null; reader.beginArray(); switch (reader.peek()) { - case STRING: - arraySet = new HashSet<>(); - while (reader.hasNext()) { - arraySet.add(reader.nextString()); - } - break; - case NUMBER: - arraySet = new HashSet<>(); - while (reader.hasNext()) { - arraySet.add(reader.nextLong()); - } - break; - default: - arraySet = new HashSet(); - break; + case STRING: + arraySet = new HashSet<>(); + while (reader.hasNext()) { + arraySet.add(reader.nextString()); + } + break; + case NUMBER: + arraySet = new HashSet<>(); + while (reader.hasNext()) { + arraySet.add(reader.nextLong()); + } + break; + default: + arraySet = new HashSet(); + break; } reader.endArray(); return arraySet; diff --git a/openid-connect-common/src/main/java/org/mitre/util/jpa/JpaUtil.java b/openid-connect-common/src/main/java/org/mitre/util/jpa/JpaUtil.java index c6fa2c6df4..15d3ea0e2d 100644 --- a/openid-connect-common/src/main/java/org/mitre/util/jpa/JpaUtil.java +++ b/openid-connect-common/src/main/java/org/mitre/util/jpa/JpaUtil.java @@ -16,48 +16,48 @@ *******************************************************************************/ package org.mitre.util.jpa; -import org.mitre.data.PageCriteria; - import java.util.List; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; +import org.mitre.data.PageCriteria; + /** * @author mfranklin * Date: 4/28/11 * Time: 2:13 PM */ public class JpaUtil { - public static T getSingleResult(List list) { + public static T getSingleResult(List list) { switch(list.size()) { - case 0: - return null; - case 1: - return list.get(0); - default: - throw new IllegalStateException("Expected single result, got " + list.size()); + case 0: + return null; + case 1: + return list.get(0); + default: + throw new IllegalStateException("Expected single result, got " + list.size()); } } - /** - * Get a page of results from the specified TypedQuery - * by using the given PageCriteria to limit the query - * results. The PageCriteria will override any size or - * offset already specified on the query. - * - * @param the type parameter - * @param query the query - * @param pageCriteria the page criteria - * @return the list - */ - public static List getResultPage(TypedQuery query, PageCriteria pageCriteria){ - query.setMaxResults(pageCriteria.getPageSize()); - query.setFirstResult(pageCriteria.getPageNumber()*pageCriteria.getPageSize()); + /** + * Get a page of results from the specified TypedQuery + * by using the given PageCriteria to limit the query + * results. The PageCriteria will override any size or + * offset already specified on the query. + * + * @param the type parameter + * @param query the query + * @param pageCriteria the page criteria + * @return the list + */ + public static List getResultPage(TypedQuery query, PageCriteria pageCriteria){ + query.setMaxResults(pageCriteria.getPageSize()); + query.setFirstResult(pageCriteria.getPageNumber()*pageCriteria.getPageSize()); - return query.getResultList(); - } + return query.getResultList(); + } public static T saveOrUpdate(I id, EntityManager entityManager, T entity) { T tmp = entityManager.merge(entity); diff --git a/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java b/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java index 46d8bf57eb..9bc11020fd 100644 --- a/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java +++ b/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java @@ -1,13 +1,13 @@ package org.mitre.data; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - import java.util.ArrayList; import java.util.Collection; import java.util.List; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -17,204 +17,200 @@ */ public class AbstractPageOperationTemplateTest { - @Before - public void setUp() throws Exception { - } - - @Test(timeout = 1000L) - public void execute_zeropages() { - CountingPageOperation op = new CountingPageOperation(0,Long.MAX_VALUE); - op.execute(); - - assertEquals(0L, op.counter); - } - - @Test(timeout = 1000L) - public void execute_singlepage() { - CountingPageOperation op = new CountingPageOperation(1,Long.MAX_VALUE); - op.execute(); - - assertEquals(10L, op.counter); - } - - @Test(timeout = 1000L) - public void execute_negpage() { - CountingPageOperation op = new CountingPageOperation(-1,Long.MAX_VALUE); - op.execute(); - - assertEquals(0L, op.counter); - } - - @Test(timeout = 1000L) - public void execute_npage(){ - int n = 7; - CountingPageOperation op = new CountingPageOperation(n,Long.MAX_VALUE); - op.execute(); - - assertEquals(n*10L, op.counter); - } - - @Test(timeout = 1000L) - public void execute_nullpage(){ - CountingPageOperation op = new NullPageCountingPageOperation(Integer.MAX_VALUE, Long.MAX_VALUE); - op.execute(); - - assertEquals(0L, op.getCounter()); - } - - @Test(timeout = 1000L) - public void execute_emptypage(){ - CountingPageOperation op = new EmptyPageCountingPageOperation(Integer.MAX_VALUE, Long.MAX_VALUE); - op.execute(); - - assertEquals(0L, op.getCounter()); - } - - @Test(timeout = 1000L) - public void execute_zerotime(){ - CountingPageOperation op = new CountingPageOperation(Integer.MAX_VALUE,0L); - op.execute(); - - assertEquals(0L, op.getCounter()); - assertEquals(0L, op.getTimeToLastFetch()); - } - - /* - * This is a valid test however it is vulnerable to a race condition - * as such it is being ignored. - */ - @Test(timeout = 1000L) - @Ignore - public void execute_nonzerotime(){ - Long timeMillis = 200L; - CountingPageOperation op = new CountingPageOperation(Integer.MAX_VALUE,timeMillis); - op.execute(); - - assertFalse("last fetch time " + op.getTimeToLastFetch() + "" + - " and previous fetch time " + op.getTimeToPreviousFetch() + - " exceed max time" + timeMillis, - op.getTimeToLastFetch() > timeMillis - && op.getTimeToPreviousFetch() > timeMillis); - } - - @Test(timeout = 1000L) - public void execute_negtime(){ - Long timeMillis = -100L; - CountingPageOperation op = new CountingPageOperation(Integer.MAX_VALUE,timeMillis); - op.execute(); - - assertEquals(0L, op.getCounter()); - } - - @Test(timeout = 1000L) - public void execute_swallowException(){ - CountingPageOperation op = new EvenExceptionCountingPageOperation(1, 1000L); - op.execute(); - - assertTrue(op.isSwallowExceptions()); - assertEquals(5L, op.getCounter()); - } - - @Test(expected = IllegalStateException.class) - public void execute_noSwallowException(){ - CountingPageOperation op = new EvenExceptionCountingPageOperation(1, 1000L); - op.setSwallowExceptions(false); - - try { - op.execute(); - }finally { - assertEquals(1L, op.getCounter()); - } - } - - - private static class CountingPageOperation extends AbstractPageOperationTemplate{ - - private int currentPageFetch; - private int pageSize = 10; - private long counter = 0L; - private long startTime; - private long timeToLastFetch; - private long timeToPreviousFetch; - - private CountingPageOperation(int maxPages, long maxTime) { - super(maxPages, maxTime, "CountingPageOperation"); - startTime = System.currentTimeMillis(); - } - - @Override - public Collection fetchPage() { - timeToPreviousFetch = timeToLastFetch > 0 ? timeToLastFetch : 0; - timeToLastFetch = System.currentTimeMillis() - startTime; - - List page = new ArrayList(pageSize); - for(int i = 0; i < pageSize; i++ ) { - page.add("item " + currentPageFetch * pageSize + i); - } - currentPageFetch++; - return page; - } - - @Override - protected void doOperation(String item) { - counter++; - } - - public long getCounter() { - return counter; - } - - public long getTimeToLastFetch() { - return timeToLastFetch; - } - - public long getTimeToPreviousFetch() { - return timeToPreviousFetch; - } - - public long getStartTime(){ - return startTime; - } - } - - private static class NullPageCountingPageOperation extends CountingPageOperation { - private NullPageCountingPageOperation(int maxPages, long maxTime) { - super(maxPages, maxTime); - } - - @Override - public Collection fetchPage() { - return null; - } - } - - private static class EmptyPageCountingPageOperation extends CountingPageOperation { - private EmptyPageCountingPageOperation(int maxPages, long maxTime) { - super(maxPages, maxTime); - } - - @Override - public Collection fetchPage() { - return new ArrayList<>(0); - } - } - - private static class EvenExceptionCountingPageOperation extends CountingPageOperation { - - private int callCounter; - private EvenExceptionCountingPageOperation(int maxPages, long maxTime) { - super(maxPages, maxTime); - } - - @Override - protected void doOperation(String item) { - callCounter++; - if(callCounter%2 == 0){ - throw new IllegalStateException("even number items cannot be processed"); - } - - super.doOperation(item); - - } - } + @Before + public void setUp() throws Exception { + } + + @Test(timeout = 1000L) + public void execute_zeropages() { + CountingPageOperation op = new CountingPageOperation(0,Long.MAX_VALUE); + op.execute(); + + assertEquals(0L, op.counter); + } + + @Test(timeout = 1000L) + public void execute_singlepage() { + CountingPageOperation op = new CountingPageOperation(1,Long.MAX_VALUE); + op.execute(); + + assertEquals(10L, op.counter); + } + + @Test(timeout = 1000L) + public void execute_negpage() { + CountingPageOperation op = new CountingPageOperation(-1,Long.MAX_VALUE); + op.execute(); + + assertEquals(0L, op.counter); + } + + @Test(timeout = 1000L) + public void execute_npage(){ + int n = 7; + CountingPageOperation op = new CountingPageOperation(n,Long.MAX_VALUE); + op.execute(); + + assertEquals(n*10L, op.counter); + } + + @Test(timeout = 1000L) + public void execute_nullpage(){ + CountingPageOperation op = new NullPageCountingPageOperation(Integer.MAX_VALUE, Long.MAX_VALUE); + op.execute(); + + assertEquals(0L, op.getCounter()); + } + + @Test(timeout = 1000L) + public void execute_emptypage(){ + CountingPageOperation op = new EmptyPageCountingPageOperation(Integer.MAX_VALUE, Long.MAX_VALUE); + op.execute(); + + assertEquals(0L, op.getCounter()); + } + + @Test(timeout = 1000L) + public void execute_zerotime(){ + CountingPageOperation op = new CountingPageOperation(Integer.MAX_VALUE,0L); + op.execute(); + + assertEquals(0L, op.getCounter()); + assertEquals(0L, op.getTimeToLastFetch()); + } + + /* + * This is a valid test however it is vulnerable to a race condition + * as such it is being ignored. + */ + @Test(timeout = 1000L) + @Ignore + public void execute_nonzerotime(){ + Long timeMillis = 200L; + CountingPageOperation op = new CountingPageOperation(Integer.MAX_VALUE,timeMillis); + op.execute(); + + assertFalse("last fetch time " + op.getTimeToLastFetch() + "" + + " and previous fetch time " + op.getTimeToPreviousFetch() + + " exceed max time" + timeMillis, + op.getTimeToLastFetch() > timeMillis + && op.getTimeToPreviousFetch() > timeMillis); + } + + @Test(timeout = 1000L) + public void execute_negtime(){ + Long timeMillis = -100L; + CountingPageOperation op = new CountingPageOperation(Integer.MAX_VALUE,timeMillis); + op.execute(); + + assertEquals(0L, op.getCounter()); + } + + @Test(timeout = 1000L) + public void execute_swallowException(){ + CountingPageOperation op = new EvenExceptionCountingPageOperation(1, 1000L); + op.execute(); + + assertTrue(op.isSwallowExceptions()); + assertEquals(5L, op.getCounter()); + } + + @Test(expected = IllegalStateException.class) + public void execute_noSwallowException(){ + CountingPageOperation op = new EvenExceptionCountingPageOperation(1, 1000L); + op.setSwallowExceptions(false); + + try { + op.execute(); + }finally { + assertEquals(1L, op.getCounter()); + } + } + + + private static class CountingPageOperation extends AbstractPageOperationTemplate{ + + private int currentPageFetch; + private int pageSize = 10; + private long counter = 0L; + private long startTime; + private long timeToLastFetch; + private long timeToPreviousFetch; + + private CountingPageOperation(int maxPages, long maxTime) { + super(maxPages, maxTime, "CountingPageOperation"); + startTime = System.currentTimeMillis(); + } + + @Override + public Collection fetchPage() { + timeToPreviousFetch = timeToLastFetch > 0 ? timeToLastFetch : 0; + timeToLastFetch = System.currentTimeMillis() - startTime; + + List page = new ArrayList(pageSize); + for(int i = 0; i < pageSize; i++ ) { + page.add("item " + currentPageFetch * pageSize + i); + } + currentPageFetch++; + return page; + } + + @Override + protected void doOperation(String item) { + counter++; + } + + public long getCounter() { + return counter; + } + + public long getTimeToLastFetch() { + return timeToLastFetch; + } + + public long getTimeToPreviousFetch() { + return timeToPreviousFetch; + } + } + + private static class NullPageCountingPageOperation extends CountingPageOperation { + private NullPageCountingPageOperation(int maxPages, long maxTime) { + super(maxPages, maxTime); + } + + @Override + public Collection fetchPage() { + return null; + } + } + + private static class EmptyPageCountingPageOperation extends CountingPageOperation { + private EmptyPageCountingPageOperation(int maxPages, long maxTime) { + super(maxPages, maxTime); + } + + @Override + public Collection fetchPage() { + return new ArrayList<>(0); + } + } + + private static class EvenExceptionCountingPageOperation extends CountingPageOperation { + + private int callCounter; + private EvenExceptionCountingPageOperation(int maxPages, long maxTime) { + super(maxPages, maxTime); + } + + @Override + protected void doOperation(String item) { + callCounter++; + if(callCounter%2 == 0){ + throw new IllegalStateException("even number items cannot be processed"); + } + + super.doOperation(item); + + } + } } diff --git a/openid-connect-common/src/test/java/org/mitre/jose/TestJWKSetKeyStore.java b/openid-connect-common/src/test/java/org/mitre/jose/TestJWKSetKeyStore.java index 120fcb2a10..d894edddd8 100644 --- a/openid-connect-common/src/test/java/org/mitre/jose/TestJWKSetKeyStore.java +++ b/openid-connect-common/src/test/java/org/mitre/jose/TestJWKSetKeyStore.java @@ -50,35 +50,35 @@ public class TestJWKSetKeyStore { private String RSAkid = "rsa_1"; private JWK RSAjwk = new RSAKey( new Base64URL("oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW" + - "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S" + - "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a" + - "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS" + - "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj" + - "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw"), // n + "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S" + + "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a" + + "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS" + + "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj" + + "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw"), // n new Base64URL("AQAB"), // e new Base64URL("kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5N" + - "WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9" + - "3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk" + - "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" + - "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" + - "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ"), // d + "WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9" + + "3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk" + + "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" + + "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" + + "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ"), // d KeyUse.ENCRYPTION, null, JWEAlgorithm.RSA_OAEP, RSAkid, null, null, null); private String RSAkid_rsa2 = "rsa_2"; private JWK RSAjwk_rsa2 = new RSAKey( new Base64URL("oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW" + - "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S" + - "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a" + - "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS" + - "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj" + - "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw"), // n + "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S" + + "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a" + + "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS" + + "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj" + + "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw"), // n new Base64URL("AQAB"), // e new Base64URL("kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5N" + - "WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9" + - "3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk" + - "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" + - "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" + - "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ"), // d + "WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9" + + "3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk" + + "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" + + "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" + + "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ"), // d KeyUse.ENCRYPTION, null, JWEAlgorithm.RSA1_5, RSAkid_rsa2, null, null, null); diff --git a/openid-connect-common/src/test/java/org/mitre/jwt/encryption/service/impl/TestDefaultJWTEncryptionAndDecryptionService.java b/openid-connect-common/src/test/java/org/mitre/jwt/encryption/service/impl/TestDefaultJWTEncryptionAndDecryptionService.java index fca1d4a24d..06f399f094 100644 --- a/openid-connect-common/src/test/java/org/mitre/jwt/encryption/service/impl/TestDefaultJWTEncryptionAndDecryptionService.java +++ b/openid-connect-common/src/test/java/org/mitre/jwt/encryption/service/impl/TestDefaultJWTEncryptionAndDecryptionService.java @@ -65,15 +65,15 @@ */ public class TestDefaultJWTEncryptionAndDecryptionService { - - private static Logger logger = LoggerFactory.getLogger(TestDefaultJWTEncryptionAndDecryptionService.class); + + private static Logger logger = LoggerFactory.getLogger(TestDefaultJWTEncryptionAndDecryptionService.class); private String plainText = "The true sign of intelligence is not knowledge but imagination."; private String issuer = "www.example.net"; private String subject = "example_user"; private JWTClaimsSet claimsSet = null; - + @Rule public ExpectedException exception = ExpectedException.none(); @@ -93,35 +93,35 @@ public class TestDefaultJWTEncryptionAndDecryptionService { private String RSAkid = "rsa321"; private JWK RSAjwk = new RSAKey( new Base64URL("oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW" + - "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S" + - "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a" + - "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS" + - "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj" + - "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw"), // n + "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S" + + "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a" + + "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS" + + "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj" + + "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw"), // n new Base64URL("AQAB"), // e new Base64URL("kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5N" + - "WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9" + - "3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk" + - "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" + - "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" + - "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ"), // d + "WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9" + + "3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk" + + "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" + + "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" + + "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ"), // d KeyUse.ENCRYPTION, null, JWEAlgorithm.RSA_OAEP, RSAkid, null, null, null); private String RSAkid_2 = "rsa3210"; private JWK RSAjwk_2 = new RSAKey( new Base64URL("oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW" + - "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S" + - "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a" + - "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS" + - "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj" + - "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw"), // n + "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S" + + "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a" + + "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS" + + "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj" + + "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw"), // n new Base64URL("AQAB"), // e new Base64URL("kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5N" + - "WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9" + - "3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk" + - "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" + - "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" + - "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ"), // d + "WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9" + + "3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk" + + "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" + + "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" + + "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ"), // d KeyUse.ENCRYPTION, null, JWEAlgorithm.RSA1_5, RSAkid_2, null, null, null); private String AESkid = "aes123"; @@ -165,9 +165,9 @@ public void prepare() throws NoSuchAlgorithmException, InvalidKeySpecException, service_4 = new DefaultJWTEncryptionAndDecryptionService(keys_4); claimsSet = new JWTClaimsSet.Builder() - .issuer(issuer) - .subject(subject) - .build(); + .issuer(issuer) + .subject(subject) + .build(); // Key Store @@ -186,7 +186,7 @@ public void decrypt_RSA() throws ParseException, NoSuchAlgorithmException { Assume.assumeTrue(JCASupport.isSupported(JWEAlgorithm.RSA_OAEP) // check for algorithm support && JCASupport.isSupported(EncryptionMethod.A256GCM) && Cipher.getMaxAllowedKeyLength("RC5") >= 256); // check for unlimited crypto strength - + service.setDefaultDecryptionKeyId(RSAkid); service.setDefaultEncryptionKeyId(RSAkid); @@ -207,7 +207,7 @@ public void encryptThenDecrypt_RSA() throws ParseException, NoSuchAlgorithmExcep Assume.assumeTrue(JCASupport.isSupported(JWEAlgorithm.RSA_OAEP) // check for algorithm support && JCASupport.isSupported(EncryptionMethod.A256GCM) && Cipher.getMaxAllowedKeyLength("RC5") >= 256); // check for unlimited crypto strength - + service.setDefaultDecryptionKeyId(RSAkid); service.setDefaultEncryptionKeyId(RSAkid); @@ -235,11 +235,11 @@ public void encryptThenDecrypt_RSA() throws ParseException, NoSuchAlgorithmExcep // The same as encryptThenDecrypt_RSA() but relies on the key from the map @Test public void encryptThenDecrypt_nullID() throws ParseException, NoSuchAlgorithmException { - + Assume.assumeTrue(JCASupport.isSupported(JWEAlgorithm.RSA_OAEP) // check for algorithm support && JCASupport.isSupported(EncryptionMethod.A256GCM) && Cipher.getMaxAllowedKeyLength("RC5") >= 256); // check for unlimited crypto strength - + service.setDefaultDecryptionKeyId(null); service.setDefaultEncryptionKeyId(null); @@ -272,7 +272,7 @@ public void encrypt_nullID_oneKey() throws NoSuchAlgorithmException { && Cipher.getMaxAllowedKeyLength("RC5") >= 256); // check for unlimited crypto strength exception.expect(IllegalStateException.class); - + service_2.setDefaultEncryptionKeyId(null); assertEquals(null, service_2.getDefaultEncryptionKeyId()); @@ -291,10 +291,10 @@ public void decrypt_nullID() throws ParseException, NoSuchAlgorithmException { Assume.assumeTrue(JCASupport.isSupported(JWEAlgorithm.RSA_OAEP) // check for algorithm support && JCASupport.isSupported(EncryptionMethod.A256GCM) && Cipher.getMaxAllowedKeyLength("RC5") >= 256); // check for unlimited crypto strength - + exception.expect(IllegalStateException.class); - + service_2.setDefaultEncryptionKeyId(RSAkid); service_2.setDefaultDecryptionKeyId(null); diff --git a/openid-connect-common/src/test/java/org/mitre/oauth2/model/ClientDetailsEntityTest.java b/openid-connect-common/src/test/java/org/mitre/oauth2/model/ClientDetailsEntityTest.java index a93aba7796..1f4887fb40 100644 --- a/openid-connect-common/src/test/java/org/mitre/oauth2/model/ClientDetailsEntityTest.java +++ b/openid-connect-common/src/test/java/org/mitre/oauth2/model/ClientDetailsEntityTest.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.model; diff --git a/openid-connect-common/src/test/java/org/mitre/oauth2/model/RegisteredClientTest.java b/openid-connect-common/src/test/java/org/mitre/oauth2/model/RegisteredClientTest.java index c00b8a3d36..3473128033 100644 --- a/openid-connect-common/src/test/java/org/mitre/oauth2/model/RegisteredClientTest.java +++ b/openid-connect-common/src/test/java/org/mitre/oauth2/model/RegisteredClientTest.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.model; diff --git a/openid-connect-common/src/test/java/org/mitre/openid/connect/ClientDetailsEntityJsonProcessorTest.java b/openid-connect-common/src/test/java/org/mitre/openid/connect/ClientDetailsEntityJsonProcessorTest.java index 93c80145d4..9e7df37d20 100644 --- a/openid-connect-common/src/test/java/org/mitre/openid/connect/ClientDetailsEntityJsonProcessorTest.java +++ b/openid-connect-common/src/test/java/org/mitre/openid/connect/ClientDetailsEntityJsonProcessorTest.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect; diff --git a/openid-connect-common/src/test/java/org/mitre/openid/connect/config/ConfigurationPropertiesBeanTest.java b/openid-connect-common/src/test/java/org/mitre/openid/connect/config/ConfigurationPropertiesBeanTest.java index 130caf399d..21f57504d2 100644 --- a/openid-connect-common/src/test/java/org/mitre/openid/connect/config/ConfigurationPropertiesBeanTest.java +++ b/openid-connect-common/src/test/java/org/mitre/openid/connect/config/ConfigurationPropertiesBeanTest.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.config; @@ -134,7 +134,7 @@ public void testCheckForHttpsIssuerHttpsTrueFlag() { } } - + @Test public void testShortTopbarTitle() { ConfigurationPropertiesBean bean = new ConfigurationPropertiesBean(); diff --git a/openid-connect-common/src/test/java/org/mitre/openid/connect/config/ServerConfigurationTest.java b/openid-connect-common/src/test/java/org/mitre/openid/connect/config/ServerConfigurationTest.java index 26c402eac9..8a4d6c51da 100644 --- a/openid-connect-common/src/test/java/org/mitre/openid/connect/config/ServerConfigurationTest.java +++ b/openid-connect-common/src/test/java/org/mitre/openid/connect/config/ServerConfigurationTest.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.config; diff --git a/openid-connect-server/src/main/java/org/mitre/discovery/view/WebfingerView.java b/openid-connect-server/src/main/java/org/mitre/discovery/view/WebfingerView.java index 0f254e5712..ef8a7f1e6d 100644 --- a/openid-connect-server/src/main/java/org/mitre/discovery/view/WebfingerView.java +++ b/openid-connect-server/src/main/java/org/mitre/discovery/view/WebfingerView.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.discovery.view; @@ -54,27 +54,27 @@ public class WebfingerView extends AbstractView { private static final Logger logger = LoggerFactory.getLogger(WebfingerView.class); private Gson gson = new GsonBuilder() - .setExclusionStrategies(new ExclusionStrategy() { - - @Override - public boolean shouldSkipField(FieldAttributes f) { - - return false; - } - - @Override - public boolean shouldSkipClass(Class clazz) { - // skip the JPA binding wrapper - if (clazz.equals(BeanPropertyBindingResult.class)) { - return true; - } - return false; - } - - }) - .serializeNulls() - .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") - .create(); + .setExclusionStrategies(new ExclusionStrategy() { + + @Override + public boolean shouldSkipField(FieldAttributes f) { + + return false; + } + + @Override + public boolean shouldSkipClass(Class clazz) { + // skip the JPA binding wrapper + if (clazz.equals(BeanPropertyBindingResult.class)) { + return true; + } + return false; + } + + }) + .serializeNulls() + .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") + .create(); @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) { diff --git a/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java b/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java index 1c034924b4..0c2df3b060 100644 --- a/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java @@ -57,9 +57,9 @@ import com.nimbusds.jose.JWSAlgorithm; /** - * + * * Handle OpenID Connect Discovery. - * + * * @author jricher * */ @@ -109,7 +109,7 @@ public String webfinger(@RequestParam("resource") String resource, @RequestParam if (!Strings.isNullOrEmpty(rel) && !rel.equals("http://openid.net/specs/connect/1.0/issuer")) { logger.warn("Responding to webfinger request for non-OIDC relation: " + rel); } - + if (!resource.equals(config.getIssuer())) { // it's not the issuer directly, need to check other methods @@ -291,16 +291,16 @@ OPTIONAL. JSON array containing a list of the JWS signing algorithms (alg values baseUrl = baseUrl.concat("/"); } - Collection serverSigningAlgs = signService.getAllSigningAlgsSupported(); - Collection clientSymmetricSigningAlgs = Lists.newArrayList(JWSAlgorithm.HS256, JWSAlgorithm.HS384, JWSAlgorithm.HS512); - Collection clientSymmetricAndAsymmetricSigningAlgs = Lists.newArrayList(JWSAlgorithm.HS256, JWSAlgorithm.HS384, JWSAlgorithm.HS512, - JWSAlgorithm.RS256, JWSAlgorithm.RS384, JWSAlgorithm.RS512, - JWSAlgorithm.ES256, JWSAlgorithm.ES384, JWSAlgorithm.ES512, + signService.getAllSigningAlgsSupported(); + Lists.newArrayList(JWSAlgorithm.HS256, JWSAlgorithm.HS384, JWSAlgorithm.HS512); + Collection clientSymmetricAndAsymmetricSigningAlgs = Lists.newArrayList(JWSAlgorithm.HS256, JWSAlgorithm.HS384, JWSAlgorithm.HS512, + JWSAlgorithm.RS256, JWSAlgorithm.RS384, JWSAlgorithm.RS512, + JWSAlgorithm.ES256, JWSAlgorithm.ES384, JWSAlgorithm.ES512, JWSAlgorithm.PS256, JWSAlgorithm.PS384, JWSAlgorithm.PS512); - Collection clientSymmetricAndAsymmetricSigningAlgsWithNone = Lists.newArrayList(JWSAlgorithm.HS256, JWSAlgorithm.HS384, JWSAlgorithm.HS512, - JWSAlgorithm.RS256, JWSAlgorithm.RS384, JWSAlgorithm.RS512, - JWSAlgorithm.ES256, JWSAlgorithm.ES384, JWSAlgorithm.ES512, - JWSAlgorithm.PS256, JWSAlgorithm.PS384, JWSAlgorithm.PS512, + Collection clientSymmetricAndAsymmetricSigningAlgsWithNone = Lists.newArrayList(JWSAlgorithm.HS256, JWSAlgorithm.HS384, JWSAlgorithm.HS512, + JWSAlgorithm.RS256, JWSAlgorithm.RS384, JWSAlgorithm.RS512, + JWSAlgorithm.ES256, JWSAlgorithm.ES384, JWSAlgorithm.ES512, + JWSAlgorithm.PS256, JWSAlgorithm.PS384, JWSAlgorithm.PS512, Algorithm.NONE); ArrayList grantTypes = Lists.newArrayList("authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer", "client_credentials", "urn:ietf:params:oauth:grant_type:redelegate", "urn:ietf:params:oauth:grant-type:device_code"); @@ -369,7 +369,7 @@ OPTIONAL. JSON array containing a list of the JWS signing algorithms (alg values m.put("code_challenge_methods_supported", Lists.newArrayList(PKCEAlgorithm.plain.getName(), PKCEAlgorithm.S256.getName())); m.put("device_authorization_endpoint", DeviceEndpoint.URL); - + model.addAttribute(JsonEntityView.ENTITY, m); return JsonEntityView.VIEWNAME; diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/assertion/AssertionOAuth2RequestFactory.java b/openid-connect-server/src/main/java/org/mitre/oauth2/assertion/AssertionOAuth2RequestFactory.java index aa20c35076..567c8c64fb 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/assertion/AssertionOAuth2RequestFactory.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/assertion/AssertionOAuth2RequestFactory.java @@ -25,7 +25,7 @@ /** * Take in an assertion and token request and generate an OAuth2Request from it, including scopes and other important components - * + * * @author jricher * */ diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/assertion/impl/DirectCopyRequestFactory.java b/openid-connect-server/src/main/java/org/mitre/oauth2/assertion/impl/DirectCopyRequestFactory.java index 97e52a89de..6dae9f7b94 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/assertion/impl/DirectCopyRequestFactory.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/assertion/impl/DirectCopyRequestFactory.java @@ -18,7 +18,6 @@ package org.mitre.oauth2.assertion.impl; import java.text.ParseException; -import java.util.List; import java.util.Set; import org.mitre.oauth2.assertion.AssertionOAuth2RequestFactory; @@ -33,10 +32,10 @@ /** * Takes an assertion from a trusted source, looks for the fields: - * + * * - scope, space-separated list of strings * - aud, array of audience IDs - * + * * @author jricher * */ @@ -51,14 +50,14 @@ public OAuth2Request createOAuth2Request(ClientDetails client, TokenRequest toke try { JWTClaimsSet claims = assertion.getJWTClaimsSet(); Set scope = OAuth2Utils.parseParameterList(claims.getStringClaim("scope")); - + Set resources = Sets.newHashSet(claims.getAudience()); - + return new OAuth2Request(tokenRequest.getRequestParameters(), client.getClientId(), client.getAuthorities(), true, scope, resources, null, null, null); } catch (ParseException e) { return null; } - + } } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/exception/AuthorizationPendingException.java b/openid-connect-server/src/main/java/org/mitre/oauth2/exception/AuthorizationPendingException.java index e2ea2f71b1..b620c9b5dc 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/exception/AuthorizationPendingException.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/exception/AuthorizationPendingException.java @@ -33,7 +33,7 @@ public AuthorizationPendingException(String msg) { } /** - * + * */ private static final long serialVersionUID = -7078098692596870940L; @@ -45,6 +45,6 @@ public String getOAuth2ErrorCode() { return "authorization_pending"; } - - + + } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/exception/DeviceCodeExpiredException.java b/openid-connect-server/src/main/java/org/mitre/oauth2/exception/DeviceCodeExpiredException.java index 6444f184a4..8a49caf0f1 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/exception/DeviceCodeExpiredException.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/exception/DeviceCodeExpiredException.java @@ -33,7 +33,7 @@ public DeviceCodeExpiredException(String msg) { } /** - * + * */ private static final long serialVersionUID = -7078098692596870940L; diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/exception/DuplicateClientIdException.java b/openid-connect-server/src/main/java/org/mitre/oauth2/exception/DuplicateClientIdException.java index 71a4733cca..c102785c78 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/exception/DuplicateClientIdException.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/exception/DuplicateClientIdException.java @@ -23,7 +23,7 @@ public DuplicateClientIdException(String clientId) { } /** - * + * */ private static final long serialVersionUID = 1L; diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthenticationHolderRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthenticationHolderRepository.java index b0de267645..48e0b6b534 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthenticationHolderRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthenticationHolderRepository.java @@ -70,15 +70,15 @@ public AuthenticationHolderEntity save(AuthenticationHolderEntity a) { @Override @Transactional(value="defaultTransactionManager") public List getOrphanedAuthenticationHolders() { - DefaultPageCriteria pageCriteria = new DefaultPageCriteria(0,MAXEXPIREDRESULTS); - return getOrphanedAuthenticationHolders(pageCriteria); + DefaultPageCriteria pageCriteria = new DefaultPageCriteria(0,MAXEXPIREDRESULTS); + return getOrphanedAuthenticationHolders(pageCriteria); } - @Override - @Transactional(value="defaultTransactionManager") - public List getOrphanedAuthenticationHolders(PageCriteria pageCriteria) { - TypedQuery query = manager.createNamedQuery(AuthenticationHolderEntity.QUERY_GET_UNUSED, AuthenticationHolderEntity.class); - return JpaUtil.getResultPage(query, pageCriteria); - } + @Override + @Transactional(value="defaultTransactionManager") + public List getOrphanedAuthenticationHolders(PageCriteria pageCriteria) { + TypedQuery query = manager.createNamedQuery(AuthenticationHolderEntity.QUERY_GET_UNUSED, AuthenticationHolderEntity.class); + return JpaUtil.getResultPage(query, pageCriteria); + } } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthorizationCodeRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthorizationCodeRepository.java index 9cdde90866..5866b9162e 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthorizationCodeRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaAuthorizationCodeRepository.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.repository.impl; @@ -35,7 +35,7 @@ /** * JPA AuthorizationCodeRepository implementation. - * + * * @author aanganes * */ @@ -92,12 +92,12 @@ public Collection getExpiredCodes() { } - @Override - public Collection getExpiredCodes(PageCriteria pageCriteria) { - TypedQuery query = manager.createNamedQuery(AuthorizationCodeEntity.QUERY_EXPIRATION_BY_DATE, AuthorizationCodeEntity.class); - query.setParameter(AuthorizationCodeEntity.PARAM_DATE, new Date()); // this gets anything that's already expired - return JpaUtil.getResultPage(query, pageCriteria); - } + @Override + public Collection getExpiredCodes(PageCriteria pageCriteria) { + TypedQuery query = manager.createNamedQuery(AuthorizationCodeEntity.QUERY_EXPIRATION_BY_DATE, AuthorizationCodeEntity.class); + query.setParameter(AuthorizationCodeEntity.PARAM_DATE, new Date()); // this gets anything that's already expired + return JpaUtil.getResultPage(query, pageCriteria); + } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java index 91f34dfccf..7f15d021b6 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.repository.impl; @@ -24,8 +24,6 @@ import java.util.Collection; import java.util.Date; -import java.util.LinkedHashSet; -import java.util.Set; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java index 3be11b3767..8624d4184f 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java @@ -52,7 +52,7 @@ public class JpaOAuth2TokenRepository implements OAuth2TokenRepository { private static final int MAXEXPIREDRESULTS = 1000; - + private static final Logger logger = LoggerFactory.getLogger(JpaOAuth2TokenRepository.class); @PersistenceContext(unitName="defaultPersistenceUnit") @@ -191,29 +191,29 @@ public List getRefreshTokensForClient(ClientDetailsEnt @Override public Set getAllExpiredAccessTokens() { - DefaultPageCriteria pageCriteria = new DefaultPageCriteria(0, MAXEXPIREDRESULTS); - return getAllExpiredAccessTokens(pageCriteria); + DefaultPageCriteria pageCriteria = new DefaultPageCriteria(0, MAXEXPIREDRESULTS); + return getAllExpiredAccessTokens(pageCriteria); } - @Override - public Set getAllExpiredAccessTokens(PageCriteria pageCriteria) { - TypedQuery query = manager.createNamedQuery(OAuth2AccessTokenEntity.QUERY_EXPIRED_BY_DATE, OAuth2AccessTokenEntity.class); - query.setParameter(OAuth2AccessTokenEntity.PARAM_DATE, new Date()); - return new LinkedHashSet<>(JpaUtil.getResultPage(query, pageCriteria)); - } + @Override + public Set getAllExpiredAccessTokens(PageCriteria pageCriteria) { + TypedQuery query = manager.createNamedQuery(OAuth2AccessTokenEntity.QUERY_EXPIRED_BY_DATE, OAuth2AccessTokenEntity.class); + query.setParameter(OAuth2AccessTokenEntity.PARAM_DATE, new Date()); + return new LinkedHashSet<>(JpaUtil.getResultPage(query, pageCriteria)); + } @Override public Set getAllExpiredRefreshTokens() { - DefaultPageCriteria pageCriteria = new DefaultPageCriteria(0, MAXEXPIREDRESULTS); - return getAllExpiredRefreshTokens(pageCriteria); + DefaultPageCriteria pageCriteria = new DefaultPageCriteria(0, MAXEXPIREDRESULTS); + return getAllExpiredRefreshTokens(pageCriteria); } - @Override - public Set getAllExpiredRefreshTokens(PageCriteria pageCriteria) { - TypedQuery query = manager.createNamedQuery(OAuth2RefreshTokenEntity.QUERY_EXPIRED_BY_DATE, OAuth2RefreshTokenEntity.class); - query.setParameter(OAuth2AccessTokenEntity.PARAM_DATE, new Date()); - return new LinkedHashSet<>(JpaUtil.getResultPage(query,pageCriteria)); - } + @Override + public Set getAllExpiredRefreshTokens(PageCriteria pageCriteria) { + TypedQuery query = manager.createNamedQuery(OAuth2RefreshTokenEntity.QUERY_EXPIRED_BY_DATE, OAuth2RefreshTokenEntity.class); + query.setParameter(OAuth2AccessTokenEntity.PARAM_DATE, new Date()); + return new LinkedHashSet<>(JpaUtil.getResultPage(query,pageCriteria)); + } @@ -276,7 +276,7 @@ public void clearDuplicateRefreshTokens() { } } - + @Override public List getAccessTokensForApprovedSite(ApprovedSite approvedSite) { TypedQuery queryA = manager.createNamedQuery(OAuth2AccessTokenEntity.QUERY_BY_APPROVED_SITE, OAuth2AccessTokenEntity.class); diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaSystemScopeRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaSystemScopeRepository.java index 23c829bbd7..b420b57bf4 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaSystemScopeRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaSystemScopeRepository.java @@ -15,10 +15,13 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.repository.impl; +import static org.mitre.util.jpa.JpaUtil.getSingleResult; +import static org.mitre.util.jpa.JpaUtil.saveOrUpdate; + import java.util.LinkedHashSet; import java.util.Set; @@ -31,9 +34,6 @@ import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import static org.mitre.util.jpa.JpaUtil.getSingleResult; -import static org.mitre.util.jpa.JpaUtil.saveOrUpdate; - /** * @author jricher * diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/BlacklistAwareRedirectResolver.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/BlacklistAwareRedirectResolver.java index c64c9cc739..2cc571a906 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/BlacklistAwareRedirectResolver.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/BlacklistAwareRedirectResolver.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.service.impl; @@ -31,10 +31,10 @@ import com.google.common.base.Strings; /** - * + * * A redirect resolver that knows how to check against the blacklisted URIs * for forbidden values. Can be configured to do strict string matching also. - * + * * @author jricher * */ @@ -46,7 +46,7 @@ public class BlacklistAwareRedirectResolver extends DefaultRedirectResolver { @Autowired private ConfigurationPropertiesBean config; - + private boolean strictMatch = false; /* (non-Javadoc) @@ -94,7 +94,7 @@ public boolean isStrictMatch() { /** * Set this to true to require exact string matches for all redirect URIs. (Default is false) - * + * * @param strictMatch the strictMatch to set */ public void setStrictMatch(boolean strictMatch) { diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java index ecd4c1684a..8020de7a63 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java @@ -24,7 +24,6 @@ import org.mitre.data.AbstractPageOperationTemplate; import org.mitre.oauth2.model.AuthenticationHolderEntity; -import org.mitre.oauth2.model.AuthorizationCodeEntity; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.DeviceCode; import org.mitre.oauth2.repository.impl.DeviceCodeRepository; @@ -41,24 +40,24 @@ */ @Service("defaultDeviceCodeService") public class DefaultDeviceCodeService implements DeviceCodeService { - + @Autowired private DeviceCodeRepository repository; - + /* (non-Javadoc) * @see org.mitre.oauth2.service.DeviceCodeService#save(org.mitre.oauth2.model.DeviceCode) */ @Override public DeviceCode createNewDeviceCode(String deviceCode, String userCode, Set requestedScopes, ClientDetailsEntity client, Map parameters) { - + DeviceCode dc = new DeviceCode(deviceCode, userCode, requestedScopes, client.getClientId(), parameters); - + if (client.getDeviceCodeValiditySeconds() != null) { dc.setExpiration(new Date(System.currentTimeMillis() + client.getDeviceCodeValiditySeconds() * 1000L)); } - + dc.setApproved(false); - + return repository.save(dc); } @@ -76,12 +75,12 @@ public DeviceCode lookUpByUserCode(String userCode) { @Override public DeviceCode approveDeviceCode(DeviceCode dc, OAuth2Authentication auth) { DeviceCode found = repository.getById(dc.getId()); - + found.setApproved(true); - + AuthenticationHolderEntity authHolder = new AuthenticationHolderEntity(); authHolder.setAuthentication(auth); - + found.setAuthenticationHolder(authHolder); return repository.save(found); @@ -93,10 +92,10 @@ public DeviceCode approveDeviceCode(DeviceCode dc, OAuth2Authentication auth) { @Override public DeviceCode consumeDeviceCode(String deviceCode, ClientDetails client) { DeviceCode found = repository.getByDeviceCode(deviceCode); - + // make sure it's not used twice repository.remove(found); - + if (found.getClientId().equals(client.getClientId())) { // make sure the client matches, if so, we're good return found; @@ -104,7 +103,7 @@ public DeviceCode consumeDeviceCode(String deviceCode, ClientDetails client) { // if the clients don't match, pretend the code wasn't found return null; } - + } /* (non-Javadoc) @@ -113,18 +112,18 @@ public DeviceCode consumeDeviceCode(String deviceCode, ClientDetails client) { @Override @Transactional(value="defaultTransactionManager") public void clearExpiredDeviceCodes() { - - new AbstractPageOperationTemplate("clearExpiredDeviceCodes"){ - @Override - public Collection fetchPage() { - return repository.getExpiredCodes(); - } - - @Override - protected void doOperation(DeviceCode item) { - repository.remove(item); - } - }.execute(); + + new AbstractPageOperationTemplate("clearExpiredDeviceCodes"){ + @Override + public Collection fetchPage() { + return repository.getExpiredCodes(); + } + + @Override + protected void doOperation(DeviceCode item) { + repository.remove(item); + } + }.execute(); } } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java index f7e2ac44bc..40314158bb 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java @@ -16,6 +16,8 @@ *******************************************************************************/ package org.mitre.oauth2.service.impl; +import static com.google.common.collect.Maps.newLinkedHashMap; + import java.text.ParseException; import java.util.Map; import java.util.Set; @@ -33,8 +35,6 @@ import com.google.common.base.Joiner; import com.google.common.collect.Sets; -import static com.google.common.collect.Maps.newLinkedHashMap; - /** * Default implementation of the {@link IntrospectionResultAssembler} interface. */ diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java index 59806caa2f..d9589bf93e 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2AuthorizationCodeService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.service.impl; @@ -39,7 +39,7 @@ /** * Database-backed, random-value authorization code service implementation. - * + * * @author aanganes * */ @@ -61,7 +61,7 @@ public class DefaultOAuth2AuthorizationCodeService implements AuthorizationCodeS /** * Generate a random authorization code and create an AuthorizationCodeEntity, * which will be stored in the repository. - * + * * @param authentication the authentication of the current user, to be retrieved when the * code is consumed * @return the authorization code @@ -90,7 +90,7 @@ public String createAuthorizationCode(OAuth2Authentication authentication) { * Match the provided string to an AuthorizationCodeEntity. If one is found, return * the authentication associated with the code. If one is not found, throw an * InvalidGrantException. - * + * * @param code the authorization code * @return the authentication that made the original request * @throws InvalidGrantException, if an AuthorizationCodeEntity is not found with the given value @@ -117,17 +117,17 @@ public OAuth2Authentication consumeAuthorizationCode(String code) throws Invalid @Transactional(value="defaultTransactionManager") public void clearExpiredAuthorizationCodes() { - new AbstractPageOperationTemplate("clearExpiredAuthorizationCodes"){ - @Override - public Collection fetchPage() { - return repository.getExpiredCodes(); - } - - @Override - protected void doOperation(AuthorizationCodeEntity item) { - repository.remove(item); - } - }.execute(); + new AbstractPageOperationTemplate("clearExpiredAuthorizationCodes"){ + @Override + public Collection fetchPage() { + return repository.getExpiredCodes(); + } + + @Override + protected void doOperation(AuthorizationCodeEntity item) { + repository.remove(item); + } + }.execute(); } /** diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ClientDetailsEntityService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ClientDetailsEntityService.java index 6b3fc94567..744b623a18 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ClientDetailsEntityService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ClientDetailsEntityService.java @@ -124,13 +124,13 @@ public ClientDetailsEntity saveNewClient(ClientDetailsEntity client) { if (Strings.isNullOrEmpty(client.getClientId())) { client = generateClientId(client); } - + // make sure that clients with the "refresh_token" grant type have the "offline_access" scope, and vice versa ensureRefreshTokenConsistency(client); // make sure we don't have both a JWKS and a JWKS URI ensureKeyConsistency(client); - + // check consistency when using HEART mode checkHeartMode(client); @@ -215,7 +215,7 @@ private void ensureRefreshTokenConsistency(ClientDetailsEntity client) { * - A redirect_uri must be registered with either authorization_code or implicit * - A key must be registered * - A client secret must not be generated - * - authorization_code and client_credentials must use the private_key authorization method + * - authorization_code and client_credentials must use the private_key authorization method * @param client */ private void checkHeartMode(ClientDetailsEntity client) { @@ -225,53 +225,53 @@ private void checkHeartMode(ClientDetailsEntity client) { if (client.getGrantTypes().contains("implicit") || client.getGrantTypes().contains("client_credentials")) { throw new IllegalArgumentException("[HEART mode] Incompatible grant types"); } - + // make sure we've got the right authentication method if (client.getTokenEndpointAuthMethod() == null || !client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY)) { throw new IllegalArgumentException("[HEART mode] Authorization code clients must use the private_key authentication method"); } - + // make sure we've got a redirect URI if (client.getRedirectUris().isEmpty()) { throw new IllegalArgumentException("[HEART mode] Authorization code clients must register at least one redirect URI"); } } - + if (client.getGrantTypes().contains("implicit")) { // make sure we don't have incompatible grant types if (client.getGrantTypes().contains("authorization_code") || client.getGrantTypes().contains("client_credentials") || client.getGrantTypes().contains("refresh_token")) { throw new IllegalArgumentException("[HEART mode] Incompatible grant types"); } - + // make sure we've got the right authentication method if (client.getTokenEndpointAuthMethod() == null || !client.getTokenEndpointAuthMethod().equals(AuthMethod.NONE)) { throw new IllegalArgumentException("[HEART mode] Implicit clients must use the none authentication method"); } - + // make sure we've got a redirect URI if (client.getRedirectUris().isEmpty()) { throw new IllegalArgumentException("[HEART mode] Implicit clients must register at least one redirect URI"); } } - + if (client.getGrantTypes().contains("client_credentials")) { // make sure we don't have incompatible grant types if (client.getGrantTypes().contains("authorization_code") || client.getGrantTypes().contains("implicit") || client.getGrantTypes().contains("refresh_token")) { throw new IllegalArgumentException("[HEART mode] Incompatible grant types"); } - + // make sure we've got the right authentication method if (client.getTokenEndpointAuthMethod() == null || !client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY)) { throw new IllegalArgumentException("[HEART mode] Client credentials clients must use the private_key authentication method"); } - + // make sure we've got a redirect URI if (!client.getRedirectUris().isEmpty()) { throw new IllegalArgumentException("[HEART mode] Client credentials clients must not register a redirect URI"); } } - + if (client.getGrantTypes().contains("password")) { throw new IllegalArgumentException("[HEART mode] Password grant type is forbidden"); } @@ -309,14 +309,14 @@ private void checkHeartMode(ClientDetailsEntity client) { customScheme = true; } } - + // now we make sure the client has a URI in only one of each of the three categories if (!((localhost ^ remoteHttps ^ customScheme) && !(localhost && remoteHttps && customScheme))) { throw new IllegalArgumentException("[HEART mode] Can't have more than one class of redirect URI"); } } - + } } @@ -386,16 +386,16 @@ public void deleteClient(ClientDetailsEntity client) throws InvalidClientExcepti /** * Update the oldClient with information from the newClient. The * id from oldClient is retained. - * + * * Checks to make sure the refresh grant type and * the scopes are set appropriately. - * + * * Checks to make sure the redirect URIs aren't blacklisted. - * + * * Attempts to load the redirect URI (possibly cached) to check the * sector identifier against the contents there. - * - * + * + * */ @Override public ClientDetailsEntity updateClient(ClientDetailsEntity oldClient, ClientDetailsEntity newClient) throws IllegalArgumentException { @@ -412,7 +412,7 @@ public ClientDetailsEntity updateClient(ClientDetailsEntity oldClient, ClientDet // make sure we don't have both a JWKS and a JWKS URI ensureKeyConsistency(newClient); - + // check consistency when using HEART mode checkHeartMode(newClient); @@ -460,7 +460,7 @@ public ClientDetailsEntity generateClientSecret(ClientDetailsEntity client) { /** * Utility class to load a sector identifier's set of authorized redirect URIs. - * + * * @author jricher * */ diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java index ad0a664919..6b9d2d9aff 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.service.impl; @@ -71,7 +71,7 @@ /** * @author jricher - * + * */ @Service("defaultOAuth2ProviderTokenService") public class DefaultOAuth2ProviderTokenService implements OAuth2TokenEntityService { @@ -157,7 +157,7 @@ private OAuth2AccessTokenEntity clearExpiredAccessToken(OAuth2AccessTokenEntity return token; } } - + /** * Utility function to delete a refresh token that's expired before returning it. * @param token the token to check @@ -175,7 +175,7 @@ private OAuth2RefreshTokenEntity clearExpiredRefreshToken(OAuth2RefreshTokenEnti return token; } } - + @Override public OAuth2AccessTokenEntity createAccessToken(OAuth2Authentication authentication) throws AuthenticationException, InvalidClientException { if (authentication != null && authentication.getOAuth2Request() != null) { @@ -188,14 +188,14 @@ public OAuth2AccessTokenEntity createAccessToken(OAuth2Authentication authentica throw new InvalidClientException("Client not found: " + request.getClientId()); } - + // handle the PKCE code challenge if present if (request.getExtensions().containsKey(CODE_CHALLENGE)) { String challenge = (String) request.getExtensions().get(CODE_CHALLENGE); PKCEAlgorithm alg = PKCEAlgorithm.parse((String) request.getExtensions().get(CODE_CHALLENGE_METHOD)); - + String verifier = request.getRequestParameters().get(CODE_VERIFIER); - + if (alg.equals(PKCEAlgorithm.plain)) { // do a direct string comparison if (!challenge.equals(verifier)) { @@ -213,10 +213,10 @@ public OAuth2AccessTokenEntity createAccessToken(OAuth2Authentication authentica logger.error("Unknown algorithm for PKCE digest", e); } } - + } - + OAuth2AccessTokenEntity token = new OAuth2AccessTokenEntity();//accessTokenFactory.createNewAccessToken(); // attach the client @@ -492,41 +492,41 @@ public List getRefreshTokensForClient(ClientDetailsEnt public void clearExpiredTokens() { logger.debug("Cleaning out all expired tokens"); - new AbstractPageOperationTemplate("clearExpiredAccessTokens") { - @Override - public Collection fetchPage() { - return tokenRepository.getAllExpiredAccessTokens(new DefaultPageCriteria()); - } - - @Override - public void doOperation(OAuth2AccessTokenEntity item) { - revokeAccessToken(item); - } - }.execute(); - - new AbstractPageOperationTemplate("clearExpiredRefreshTokens") { - @Override - public Collection fetchPage() { - return tokenRepository.getAllExpiredRefreshTokens(new DefaultPageCriteria()); - } - - @Override - public void doOperation(OAuth2RefreshTokenEntity item) { - revokeRefreshToken(item); - } - }.execute(); - - new AbstractPageOperationTemplate("clearExpiredAuthenticationHolders") { - @Override - public Collection fetchPage() { - return authenticationHolderRepository.getOrphanedAuthenticationHolders(new DefaultPageCriteria()); - } - - @Override - public void doOperation(AuthenticationHolderEntity item) { - authenticationHolderRepository.remove(item); - } - }.execute(); + new AbstractPageOperationTemplate("clearExpiredAccessTokens") { + @Override + public Collection fetchPage() { + return tokenRepository.getAllExpiredAccessTokens(new DefaultPageCriteria()); + } + + @Override + public void doOperation(OAuth2AccessTokenEntity item) { + revokeAccessToken(item); + } + }.execute(); + + new AbstractPageOperationTemplate("clearExpiredRefreshTokens") { + @Override + public Collection fetchPage() { + return tokenRepository.getAllExpiredRefreshTokens(new DefaultPageCriteria()); + } + + @Override + public void doOperation(OAuth2RefreshTokenEntity item) { + revokeRefreshToken(item); + } + }.execute(); + + new AbstractPageOperationTemplate("clearExpiredAuthenticationHolders") { + @Override + public Collection fetchPage() { + return authenticationHolderRepository.getOrphanedAuthenticationHolders(new DefaultPageCriteria()); + } + + @Override + public void doOperation(AuthenticationHolderEntity item) { + authenticationHolderRepository.remove(item); + } + }.execute(); } /* (non-Javadoc) @@ -535,12 +535,12 @@ public void doOperation(AuthenticationHolderEntity item) { @Override public OAuth2AccessTokenEntity saveAccessToken(OAuth2AccessTokenEntity accessToken) { OAuth2AccessTokenEntity newToken = tokenRepository.saveAccessToken(accessToken); - + // if the old token has any additional information for the return from the token endpoint, carry it through here after save if (accessToken.getAdditionalInformation() != null && !accessToken.getAdditionalInformation().isEmpty()) { newToken.getAdditionalInformation().putAll(accessToken.getAdditionalInformation()); } - + return newToken; } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultSystemScopeService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultSystemScopeService.java index 7f96c1de9b..0c9debcbb9 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultSystemScopeService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultSystemScopeService.java @@ -15,12 +15,11 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.service.impl; import java.util.LinkedHashSet; -import java.util.List; import java.util.Set; import org.mitre.oauth2.model.SystemScope; @@ -30,13 +29,10 @@ import org.springframework.stereotype.Service; import com.google.common.base.Function; -import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.base.Predicates; -import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.Collections2; -import com.google.common.collect.Lists; import com.google.common.collect.Sets; /** diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/token/ChainedTokenGranter.java b/openid-connect-server/src/main/java/org/mitre/oauth2/token/ChainedTokenGranter.java index acb36a5d2c..00bf53a946 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/token/ChainedTokenGranter.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/token/ChainedTokenGranter.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.token; diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java b/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java index 7a47aa19ba..c9a6c24310 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java @@ -37,9 +37,9 @@ /** * Implements https://tools.ietf.org/html/draft-ietf-oauth-device-flow - * + * * @see DeviceEndpoint - * + * * @author jricher * */ @@ -47,7 +47,7 @@ public class DeviceTokenGranter extends AbstractTokenGranter { public static final String GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code"; - + @Autowired private DeviceCodeService deviceCodeService; @@ -68,37 +68,37 @@ protected DeviceTokenGranter(AuthorizationServerTokenServices tokenServices, Cli protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { String deviceCode = tokenRequest.getRequestParameters().get("device_code"); - + // look up the device code and consume it DeviceCode dc = deviceCodeService.consumeDeviceCode(deviceCode, client); - + if (dc != null) { - + // make sure the code hasn't expired yet if (dc.getExpiration() != null && dc.getExpiration().before(new Date())) { // TODO: return an error throw new DeviceCodeExpiredException("Device code has expired " + deviceCode); - + } else if (!dc.isApproved()) { - + // still waiting for approval throw new AuthorizationPendingException("Authorization pending for code " + deviceCode); - + } else { // inherit the (approved) scopes from the original request tokenRequest.setScope(dc.getScope()); - + OAuth2Authentication auth = new OAuth2Authentication(getRequestFactory().createOAuth2Request(client, tokenRequest), dc.getAuthenticationHolder().getUserAuth()); - + return auth; } } else { throw new InvalidGrantException("Invalid device code: " + deviceCode); } - + } - - - + + + } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/token/JWTAssertionTokenGranter.java b/openid-connect-server/src/main/java/org/mitre/oauth2/token/JWTAssertionTokenGranter.java index fd0383df20..bcbcc4c450 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/token/JWTAssertionTokenGranter.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/token/JWTAssertionTokenGranter.java @@ -15,19 +15,17 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.token; import java.text.ParseException; import org.mitre.jwt.assertion.AssertionValidator; -import org.mitre.jwt.signer.service.JWTSigningAndValidationService; import org.mitre.oauth2.assertion.AssertionOAuth2RequestFactory; import org.mitre.oauth2.service.ClientDetailsEntityService; import org.mitre.oauth2.service.OAuth2TokenEntityService; import org.mitre.openid.connect.assertion.JWTBearerAssertionAuthenticationToken; -import org.mitre.openid.connect.config.ConfigurationPropertiesBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.core.AuthenticationException; @@ -51,26 +49,16 @@ public class JWTAssertionTokenGranter extends AbstractTokenGranter { private static final String grantType = "urn:ietf:params:oauth:grant-type:jwt-bearer"; - // keep down-cast versions so we can get to the right queries - private OAuth2TokenEntityService tokenServices; - - @Autowired - private JWTSigningAndValidationService jwtService; - - @Autowired - private ConfigurationPropertiesBean config; - @Autowired @Qualifier("jwtAssertionValidator") private AssertionValidator validator; - + @Autowired private AssertionOAuth2RequestFactory assertionFactory; @Autowired public JWTAssertionTokenGranter(OAuth2TokenEntityService tokenServices, ClientDetailsEntityService clientDetailsService, OAuth2RequestFactory requestFactory) { super(tokenServices, clientDetailsService, requestFactory, grantType); - this.tokenServices = tokenServices; } /* (non-Javadoc) @@ -82,23 +70,23 @@ protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, Tok try { String incomingAssertionValue = tokenRequest.getRequestParameters().get("assertion"); JWT assertion = JWTParser.parse(incomingAssertionValue); - + if (validator.isValid(assertion)) { - + // our validator says it's OK, time to make a token from it // the real work happens in the assertion factory and the token services return new OAuth2Authentication(assertionFactory.createOAuth2Request(client, tokenRequest, assertion), new JWTBearerAssertionAuthenticationToken(assertion, client.getAuthorities())); - + } else { logger.warn("Incoming assertion did not pass validator, rejecting"); return null; } - + } catch (ParseException e) { logger.warn("Unable to parse incoming assertion"); } - + // if we had made a token, we'd have returned it by now, so return null here to close out with no created token return null; diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/token/ScopeServiceAwareOAuth2RequestValidator.java b/openid-connect-server/src/main/java/org/mitre/oauth2/token/ScopeServiceAwareOAuth2RequestValidator.java index a0b3581e11..f6cb91da4c 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/token/ScopeServiceAwareOAuth2RequestValidator.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/token/ScopeServiceAwareOAuth2RequestValidator.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.token; @@ -30,12 +30,12 @@ import org.springframework.security.oauth2.provider.TokenRequest; /** - * + * * Validates the scopes on a request by comparing them against a client's * allowed scopes, but allow custom scopes to function through the system scopes - * + * * @author jricher - * + * */ public class ScopeServiceAwareOAuth2RequestValidator implements OAuth2RequestValidator { diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/view/TokenApiView.java b/openid-connect-server/src/main/java/org/mitre/oauth2/view/TokenApiView.java index b37aebc108..d668605e6f 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/view/TokenApiView.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/view/TokenApiView.java @@ -56,71 +56,71 @@ public class TokenApiView extends AbstractView { private static final Logger logger = LoggerFactory.getLogger(TokenApiView.class); private Gson gson = new GsonBuilder() - .setExclusionStrategies(new ExclusionStrategy() { + .setExclusionStrategies(new ExclusionStrategy() { - @Override - public boolean shouldSkipField(FieldAttributes f) { - return false; - } + @Override + public boolean shouldSkipField(FieldAttributes f) { + return false; + } - @Override - public boolean shouldSkipClass(Class clazz) { - // skip the JPA binding wrapper - if (clazz.equals(BeanPropertyBindingResult.class)) { - return true; - } - return false; - } + @Override + public boolean shouldSkipClass(Class clazz) { + // skip the JPA binding wrapper + if (clazz.equals(BeanPropertyBindingResult.class)) { + return true; + } + return false; + } - }) - .registerTypeAdapter(OAuth2AccessTokenEntity.class, new JsonSerializer() { + }) + .registerTypeAdapter(OAuth2AccessTokenEntity.class, new JsonSerializer() { - @Override - public JsonElement serialize(OAuth2AccessTokenEntity src, - Type typeOfSrc, JsonSerializationContext context) { + @Override + public JsonElement serialize(OAuth2AccessTokenEntity src, + Type typeOfSrc, JsonSerializationContext context) { - JsonObject o = new JsonObject(); + JsonObject o = new JsonObject(); - o.addProperty("value", src.getValue()); - o.addProperty("id", src.getId()); - o.addProperty("refreshTokenId", src.getRefreshToken() != null ? src.getRefreshToken().getId() : null); + o.addProperty("value", src.getValue()); + o.addProperty("id", src.getId()); + o.addProperty("refreshTokenId", src.getRefreshToken() != null ? src.getRefreshToken().getId() : null); - o.add("scopes", context.serialize(src.getScope())); + o.add("scopes", context.serialize(src.getScope())); - o.addProperty("clientId", src.getClient().getClientId()); - o.addProperty("userId", src.getAuthenticationHolder().getAuthentication().getName()); + o.addProperty("clientId", src.getClient().getClientId()); + o.addProperty("userId", src.getAuthenticationHolder().getAuthentication().getName()); - o.add("expiration", context.serialize(src.getExpiration())); + o.add("expiration", context.serialize(src.getExpiration())); - return o; - } + return o; + } - }) - .registerTypeAdapter(OAuth2RefreshTokenEntity.class, new JsonSerializer() { + }) + .registerTypeAdapter(OAuth2RefreshTokenEntity.class, new JsonSerializer() { - @Override - public JsonElement serialize(OAuth2RefreshTokenEntity src, - Type typeOfSrc, JsonSerializationContext context) { - JsonObject o = new JsonObject(); + @Override + public JsonElement serialize(OAuth2RefreshTokenEntity src, + Type typeOfSrc, JsonSerializationContext context) { + JsonObject o = new JsonObject(); - o.addProperty("value", src.getValue()); - o.addProperty("id", src.getId()); + o.addProperty("value", src.getValue()); + o.addProperty("id", src.getId()); - o.add("scopes", context.serialize(src.getAuthenticationHolder().getAuthentication().getOAuth2Request().getScope())); + o.add("scopes", context.serialize(src.getAuthenticationHolder().getAuthentication().getOAuth2Request().getScope())); - o.addProperty("clientId", src.getClient().getClientId()); - o.addProperty("userId", src.getAuthenticationHolder().getAuthentication().getName()); + o.addProperty("clientId", src.getClient().getClientId()); + o.addProperty("userId", src.getAuthenticationHolder().getAuthentication().getName()); - o.add("expiration", context.serialize(src.getExpiration())); + o.add("expiration", context.serialize(src.getExpiration())); - return o; - } + return o; + } - }) - .serializeNulls() - .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") - .create(); + }) + .serializeNulls() + .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") + .create(); @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) { diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/AuthenticationUtilities.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/AuthenticationUtilities.java index 281e5661fb..2d148fda9b 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/AuthenticationUtilities.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/AuthenticationUtilities.java @@ -25,9 +25,9 @@ import com.google.common.collect.ImmutableSet; /** - * + * * Utility class to enforce OAuth scopes in authenticated requests. - * + * * @author jricher * */ diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/CorsFilter.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/CorsFilter.java index b9e7d5fa0e..c4fd9124ae 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/CorsFilter.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/CorsFilter.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.web; @@ -30,11 +30,11 @@ import org.springframework.web.filter.OncePerRequestFilter; /** - * + * * Implements Cross-Origin Resource Sharing (CORS) headers. This filter adds the CORS * headers to all requests that pass through it, and as such it should be used only * on endpoints that require CORS support. - * + * * @author jricher * */ diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java index a14f7dcb0b..f141e8cce5 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java @@ -46,7 +46,6 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.common.exceptions.InvalidClientException; -import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; import org.springframework.security.oauth2.common.util.OAuth2Utils; import org.springframework.security.oauth2.common.util.RandomValueStringGenerator; import org.springframework.security.oauth2.provider.AuthorizationRequest; @@ -59,7 +58,6 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; /** @@ -75,41 +73,41 @@ public class DeviceEndpoint { public static final String URL = "devicecode"; public static final String USER_URL = "device"; - + public static final Logger logger = LoggerFactory.getLogger(DeviceEndpoint.class); - + @Autowired private ClientDetailsEntityService clientService; - + @Autowired private SystemScopeService scopeService; - + @Autowired private ConfigurationPropertiesBean config; - + @Autowired private DeviceCodeService deviceCodeService; - + @Autowired private OAuth2RequestFactory oAuth2RequestFactory; - + private RandomValueStringGenerator randomGenerator = new RandomValueStringGenerator(); - + @RequestMapping(value = "/" + URL, method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public String requestDeviceCode(@RequestParam("client_id") String clientId, @RequestParam(name="scope", required=false) String scope, Map parameters, ModelMap model) { - + ClientDetailsEntity client; try { client = clientService.loadClientByClientId(clientId); - + // make sure this client can do the device flow - + Collection authorizedGrantTypes = client.getAuthorizedGrantTypes(); if (authorizedGrantTypes != null && !authorizedGrantTypes.isEmpty() && !authorizedGrantTypes.contains(DeviceTokenGranter.GRANT_TYPE)) { throw new InvalidClientException("Unauthorized grant type: " + DeviceTokenGranter.GRANT_TYPE); } - + } catch (IllegalArgumentException e) { logger.error("IllegalArgumentException was thrown when attempting to load client", e); model.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST); @@ -125,7 +123,7 @@ public String requestDeviceCode(@RequestParam("client_id") String clientId, @Req // make sure the client is allowed to ask for those scopes Set requestedScopes = OAuth2Utils.parseParameterList(scope); Set allowedScopes = client.getScope(); - + if (!scopeService.scopesMatch(allowedScopes, requestedScopes)) { // client asked for scopes it can't have logger.error("Client asked for " + requestedScopes + " but is allowed " + allowedScopes); @@ -133,17 +131,17 @@ public String requestDeviceCode(@RequestParam("client_id") String clientId, @Req model.put(JsonErrorView.ERROR, "invalid_scope"); return JsonErrorView.VIEWNAME; } - + // if we got here the request is legit - + // create a device code, should be big and random String deviceCode = UUID.randomUUID().toString(); - + // create a user code, should be random but small and typable String userCode = randomGenerator.generate(); - DeviceCode dc = deviceCodeService.createNewDeviceCode(deviceCode, userCode, requestedScopes, client, parameters); - + deviceCodeService.createNewDeviceCode(deviceCode, userCode, requestedScopes, client, parameters); + Map response = new HashMap<>(); response.put("device_code", deviceCode); response.put("user_code", userCode); @@ -151,37 +149,37 @@ public String requestDeviceCode(@RequestParam("client_id") String clientId, @Req if (client.getDeviceCodeValiditySeconds() != null) { response.put("expires_in", client.getDeviceCodeValiditySeconds()); } - + model.put(JsonEntityView.ENTITY, response); - + return JsonEntityView.VIEWNAME; - + } @PreAuthorize("hasRole('ROLE_USER')") @RequestMapping(value = "/" + USER_URL, method = RequestMethod.GET) public String requestUserCode(ModelMap model) { - + // print out a page that asks the user to enter their user code // user must be logged in - + return "requestUserCode"; } - + @PreAuthorize("hasRole('ROLE_USER')") @RequestMapping(value = "/" + USER_URL + "/verify", method = RequestMethod.POST) public String readUserCode(@RequestParam("user_code") String userCode, ModelMap model, HttpSession session) { // look up the request based on the user code DeviceCode dc = deviceCodeService.lookUpByUserCode(userCode); - + // we couldn't find the device code if (dc == null) { model.addAttribute("error", "noUserCode"); return "requestUserCode"; } - + // make sure the code hasn't expired yet if (dc.getExpiration() != null && dc.getExpiration().before(new Date())) { model.addAttribute("error", "expiredUserCode"); @@ -193,12 +191,12 @@ public String readUserCode(@RequestParam("user_code") String userCode, ModelMap model.addAttribute("error", "userCodeAlreadyApproved"); return "requestUserCode"; } - + ClientDetailsEntity client = clientService.loadClientByClientId(dc.getClientId()); - + model.put("client", client); model.put("dc", dc); - + // pre-process the scopes Set scopes = scopeService.fromStrings(dc.getScope()); @@ -221,17 +219,17 @@ public String readUserCode(@RequestParam("user_code") String userCode, ModelMap session.setAttribute("authorizationRequest", authorizationRequest); session.setAttribute("deviceCode", dc); - + return "approveDevice"; } - + @PreAuthorize("hasRole('ROLE_USER')") @RequestMapping(value = "/" + USER_URL + "/approve", method = RequestMethod.POST) public String approveDevice(@RequestParam("user_code") String userCode, @RequestParam(value = "user_oauth_approval") Boolean approve, ModelMap model, Authentication auth, HttpSession session) { - + AuthorizationRequest authorizationRequest = (AuthorizationRequest) session.getAttribute("authorizationRequest"); DeviceCode dc = (DeviceCode) session.getAttribute("deviceCode"); - + // make sure the form that was submitted is the one that we were expecting if (!dc.getUserCode().equals(userCode)) { model.addAttribute("error", "userCodeMismatch"); @@ -243,11 +241,11 @@ public String approveDevice(@RequestParam("user_code") String userCode, @Request model.addAttribute("error", "expiredUserCode"); return "requestUserCode"; } - + ClientDetailsEntity client = clientService.loadClientByClientId(dc.getClientId()); - + model.put("client", client); - + // user did not approve if (!approve) { model.addAttribute("approved", false); @@ -278,7 +276,7 @@ public String approveDevice(@RequestParam("user_code") String userCode, @Request model.put("scopes", sortedScopes); model.put("approved", true); - + return "deviceApproved"; } } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/IntrospectionEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/IntrospectionEndpoint.java index 1bcc736c2b..51832ec370 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/IntrospectionEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/IntrospectionEndpoint.java @@ -16,6 +16,8 @@ *******************************************************************************/ package org.mitre.oauth2.web; +import static org.mitre.oauth2.web.AuthenticationUtilities.ensureOAuthScope; + import java.util.Collection; import java.util.HashSet; import java.util.Map; @@ -49,13 +51,11 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; -import static org.mitre.oauth2.web.AuthenticationUtilities.ensureOAuthScope; - @Controller public class IntrospectionEndpoint { /** - * + * */ public static final String URL = "introspect"; diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuth2ExceptionHandler.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuth2ExceptionHandler.java index 74cdfdaaae..7bbf2a544d 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuth2ExceptionHandler.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuth2ExceptionHandler.java @@ -28,7 +28,7 @@ /** * Controller helper that handles OAuth2 exceptions and propagates them as JSON errors. - * + * * @author jricher * */ diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuthConfirmationController.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuthConfirmationController.java index 39d356c85d..117d7f49a2 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuthConfirmationController.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuthConfirmationController.java @@ -15,10 +15,13 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.web; +import static org.mitre.openid.connect.request.ConnectRequestParameters.PROMPT; +import static org.mitre.openid.connect.request.ConnectRequestParameters.PROMPT_SEPARATOR; + import java.net.URISyntaxException; import java.security.Principal; import java.util.Date; @@ -57,9 +60,6 @@ import com.google.common.collect.Sets; import com.google.gson.JsonObject; -import static org.mitre.openid.connect.request.ConnectRequestParameters.PROMPT; -import static org.mitre.openid.connect.request.ConnectRequestParameters.PROMPT_SEPARATOR; - /** * @author jricher * diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/RevocationEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/RevocationEndpoint.java index 7c9dab0918..3b1c6b79cc 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/RevocationEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/RevocationEndpoint.java @@ -18,8 +18,6 @@ import static org.mitre.oauth2.web.AuthenticationUtilities.ensureOAuthScope; -import java.util.Collection; - import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.oauth2.model.OAuth2RefreshTokenEntity; @@ -27,7 +25,6 @@ import org.mitre.oauth2.service.OAuth2TokenEntityService; import org.mitre.oauth2.service.SystemScopeService; import org.mitre.openid.connect.view.HttpCodeView; -import org.mitre.uma.model.ResourceSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -36,7 +33,6 @@ import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @@ -94,18 +90,18 @@ public String revoke(@RequestParam("token") String tokenValue, @RequestParam(val // client acting on its own, make sure it owns the token if (!accessToken.getClient().getClientId().equals(authClient.getClientId())) { // trying to revoke a token we don't own, throw a 403 - + logger.info("Client " + authClient.getClientId() + " tried to revoke a token owned by " + accessToken.getClient().getClientId()); - + model.addAttribute(HttpCodeView.CODE, HttpStatus.FORBIDDEN); return HttpCodeView.VIEWNAME; } // if we got this far, we're allowed to do this tokenServices.revokeAccessToken(accessToken); - + logger.debug("Client " + authClient.getClientId() + " revoked access token " + tokenValue); - + model.addAttribute(HttpCodeView.CODE, HttpStatus.OK); return HttpCodeView.VIEWNAME; @@ -118,18 +114,18 @@ public String revoke(@RequestParam("token") String tokenValue, @RequestParam(val // client acting on its own, make sure it owns the token if (!refreshToken.getClient().getClientId().equals(authClient.getClientId())) { // trying to revoke a token we don't own, throw a 403 - + logger.info("Client " + authClient.getClientId() + " tried to revoke a token owned by " + refreshToken.getClient().getClientId()); - + model.addAttribute(HttpCodeView.CODE, HttpStatus.FORBIDDEN); return HttpCodeView.VIEWNAME; } // if we got this far, we're allowed to do this tokenServices.revokeRefreshToken(refreshToken); - + logger.debug("Client " + authClient.getClientId() + " revoked access token " + tokenValue); - + model.addAttribute(HttpCodeView.CODE, HttpStatus.OK); return HttpCodeView.VIEWNAME; @@ -138,7 +134,7 @@ public String revoke(@RequestParam("token") String tokenValue, @RequestParam(val // neither token type was found, simply say "OK" and be on our way. logger.debug("Failed to revoke token " + tokenValue); - + model.addAttribute(HttpCodeView.CODE, HttpStatus.OK); return HttpCodeView.VIEWNAME; } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/ScopeAPI.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/ScopeAPI.java index 6cb6f4f411..9b33e4ae85 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/ScopeAPI.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/ScopeAPI.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.oauth2.web; diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerAssertionAuthenticationToken.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerAssertionAuthenticationToken.java index c544c7eb7d..6022cabaa5 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerAssertionAuthenticationToken.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerAssertionAuthenticationToken.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.assertion; @@ -34,7 +34,7 @@ public class JWTBearerAssertionAuthenticationToken extends AbstractAuthenticationToken { /** - * + * */ private static final long serialVersionUID = -3138213539914074617L; private String subject; diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerAuthenticationProvider.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerAuthenticationProvider.java index c1584d1790..c9c394dd7b 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerAuthenticationProvider.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerAuthenticationProvider.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.assertion; @@ -118,17 +118,17 @@ public Authentication authenticate(Authentication authentication) throws Authent || alg.equals(JWSAlgorithm.ES512) || alg.equals(JWSAlgorithm.PS256) || alg.equals(JWSAlgorithm.PS384) - || alg.equals(JWSAlgorithm.PS512))) - || (client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_JWT) && - (alg.equals(JWSAlgorithm.HS256) - || alg.equals(JWSAlgorithm.HS384) - || alg.equals(JWSAlgorithm.HS512)))) { + || alg.equals(JWSAlgorithm.PS512))) + || (client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_JWT) && + (alg.equals(JWSAlgorithm.HS256) + || alg.equals(JWSAlgorithm.HS384) + || alg.equals(JWSAlgorithm.HS512)))) { // double-check the method is asymmetrical if we're in HEART mode if (config.isHeartMode() && !client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY)) { throw new AuthenticationServiceException("[HEART mode] Invalid authentication method"); } - + JWTSigningAndValidationService validator = validators.getValidator(client, alg); if (validator == null) { diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerClientAssertionTokenEndpointFilter.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerClientAssertionTokenEndpointFilter.java index 24f7b309fe..cfd69ade1b 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerClientAssertionTokenEndpointFilter.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerClientAssertionTokenEndpointFilter.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.assertion; @@ -44,7 +44,7 @@ /** * Filter to check client authentication via JWT Bearer assertions. - * + * * @author jricher * */ diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/config/JsonMessageSource.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/config/JsonMessageSource.java index 49bf99855f..37744b985d 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/config/JsonMessageSource.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/config/JsonMessageSource.java @@ -54,10 +54,10 @@ public class JsonMessageSource extends AbstractMessageSource { private Locale fallbackLocale = new Locale("en"); // US English is the fallback language private Map> languageMaps = new HashMap<>(); - + @Autowired private ConfigurationPropertiesBean config; - + @Override protected MessageFormat resolveCode(String code, Locale locale) { @@ -78,7 +78,7 @@ protected MessageFormat resolveCode(String code, Locale locale) { // otherwise format the message return new MessageFormat(value, locale); } - + } /** @@ -92,7 +92,7 @@ private String getValue(String code, List langs) { // no language maps, nothing to look up return null; } - + for (JsonObject lang : langs) { String value = getValue(code, lang); if (value != null) { @@ -100,11 +100,11 @@ private String getValue(String code, List langs) { return value; } } - + // if we didn't find anything return null return null; } - + /** * Get a value from a single map * @param code @@ -165,21 +165,21 @@ private List getLanguageMap(Locale locale) { for (String namespace : config.getLanguageNamespaces()) { // full locale string, e.g. "en_US" String filename = locale.getLanguage() + "_" + locale.getCountry() + File.separator + namespace + ".json"; - + Resource r = getBaseDirectory().createRelative(filename); - + if (!r.exists()) { // fallback to language only logger.debug("Fallback locale to language only."); filename = locale.getLanguage() + File.separator + namespace + ".json"; r = getBaseDirectory().createRelative(filename); } - + logger.info("No locale loaded, trying to load from " + r); - + JsonParser parser = new JsonParser(); JsonObject obj = (JsonObject) parser.parse(new InputStreamReader(r.getInputStream(), "UTF-8")); - + set.add(obj); } languageMaps.put(locale, set); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/filter/AuthorizationRequestFilter.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/filter/AuthorizationRequestFilter.java index 46f00e43f2..84f7735bb4 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/filter/AuthorizationRequestFilter.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/filter/AuthorizationRequestFilter.java @@ -15,11 +15,19 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.filter; -import static org.mitre.openid.connect.request.ConnectRequestParameters.*; +import static org.mitre.openid.connect.request.ConnectRequestParameters.ERROR; +import static org.mitre.openid.connect.request.ConnectRequestParameters.LOGIN_HINT; +import static org.mitre.openid.connect.request.ConnectRequestParameters.LOGIN_REQUIRED; +import static org.mitre.openid.connect.request.ConnectRequestParameters.MAX_AGE; +import static org.mitre.openid.connect.request.ConnectRequestParameters.PROMPT; +import static org.mitre.openid.connect.request.ConnectRequestParameters.PROMPT_LOGIN; +import static org.mitre.openid.connect.request.ConnectRequestParameters.PROMPT_NONE; +import static org.mitre.openid.connect.request.ConnectRequestParameters.PROMPT_SEPARATOR; +import static org.mitre.openid.connect.request.ConnectRequestParameters.STATE; import java.io.IOException; import java.net.URISyntaxException; @@ -85,11 +93,11 @@ public class AuthorizationRequestFilter extends GenericFilterBean { @Autowired(required = false) private LoginHintExtracter loginHintExtracter = new RemoveLoginHintsWithHTTP(); - + private RequestMatcher requestMatcher = new AntPathRequestMatcher("/authorize"); /** - * + * */ @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/filter/MultiUrlRequestMatcher.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/filter/MultiUrlRequestMatcher.java index c15fdd606a..70dda4fd4c 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/filter/MultiUrlRequestMatcher.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/filter/MultiUrlRequestMatcher.java @@ -27,8 +27,6 @@ import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; -import com.google.common.collect.ImmutableSet; - /** * @author jricher * @@ -43,7 +41,7 @@ public MultiUrlRequestMatcher(Set filterProcessesUrls) { Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid URL"); matchers.add(new AntPathRequestMatcher(filterProcessesUrl)); } - + } @Override @@ -53,7 +51,7 @@ public boolean matches(HttpServletRequest request) { return true; } } - + return false; } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaAddressRepository.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaAddressRepository.java index bd0dc5a25c..51f3adcb7f 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaAddressRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaAddressRepository.java @@ -26,9 +26,9 @@ /** * JPA Address repository implementation - * + * * @author Michael Joseph Walsh - * + * */ @Repository public class JpaAddressRepository implements AddressRepository { diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java index d7ea0b59a4..55372c6987 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaApprovedSiteRepository.java @@ -16,6 +16,8 @@ *******************************************************************************/ package org.mitre.openid.connect.repository.impl; +import static org.mitre.util.jpa.JpaUtil.saveOrUpdate; + import java.util.Collection; import javax.persistence.EntityManager; @@ -27,11 +29,9 @@ import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import static org.mitre.util.jpa.JpaUtil.saveOrUpdate; - /** * JPA ApprovedSite repository implementation - * + * * @author Michael Joseph Walsh, aanganes * */ diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaBlacklistedSiteRepository.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaBlacklistedSiteRepository.java index b8a6b08cf9..67c1eb9205 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaBlacklistedSiteRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaBlacklistedSiteRepository.java @@ -15,10 +15,12 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.repository.impl; +import static org.mitre.util.jpa.JpaUtil.saveOrUpdate; + import java.util.Collection; import javax.persistence.EntityManager; @@ -30,8 +32,6 @@ import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import static org.mitre.util.jpa.JpaUtil.saveOrUpdate; - /** * @author jricher * diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaPairwiseIdentifierRepository.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaPairwiseIdentifierRepository.java index 465050ae58..b8be0b66d6 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaPairwiseIdentifierRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaPairwiseIdentifierRepository.java @@ -15,10 +15,13 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.repository.impl; +import static org.mitre.util.jpa.JpaUtil.getSingleResult; +import static org.mitre.util.jpa.JpaUtil.saveOrUpdate; + import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; @@ -28,9 +31,6 @@ import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import static org.mitre.util.jpa.JpaUtil.getSingleResult; -import static org.mitre.util.jpa.JpaUtil.saveOrUpdate; - /** * @author jricher * diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaUserInfoRepository.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaUserInfoRepository.java index 4db05ebfb7..eadf881780 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaUserInfoRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaUserInfoRepository.java @@ -16,6 +16,8 @@ *******************************************************************************/ package org.mitre.openid.connect.repository.impl; +import static org.mitre.util.jpa.JpaUtil.getSingleResult; + import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; @@ -25,11 +27,9 @@ import org.mitre.openid.connect.repository.UserInfoRepository; import org.springframework.stereotype.Repository; -import static org.mitre.util.jpa.JpaUtil.getSingleResult; - /** * JPA UserInfo repository implementation - * + * * @author Michael Joseph Walsh * */ diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaWhitelistedSiteRepository.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaWhitelistedSiteRepository.java index 63552bde91..c571fb6b2e 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaWhitelistedSiteRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/repository/impl/JpaWhitelistedSiteRepository.java @@ -16,6 +16,8 @@ *******************************************************************************/ package org.mitre.openid.connect.repository.impl; +import static org.mitre.util.jpa.JpaUtil.saveOrUpdate; + import java.util.Collection; import javax.persistence.EntityManager; @@ -28,11 +30,9 @@ import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import static org.mitre.util.jpa.JpaUtil.saveOrUpdate; - /** * JPA WhitelistedSite repository implementation - * + * * @author Michael Joseph Walsh, aanganes * */ diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectOAuth2RequestFactory.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectOAuth2RequestFactory.java index 644ed19e7c..a71aa6c6fc 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectOAuth2RequestFactory.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectOAuth2RequestFactory.java @@ -17,9 +17,11 @@ package org.mitre.openid.connect.request; -import static org.mitre.openid.connect.request.ConnectRequestParameters.*; +import static org.mitre.openid.connect.request.ConnectRequestParameters.AUD; import static org.mitre.openid.connect.request.ConnectRequestParameters.CLAIMS; import static org.mitre.openid.connect.request.ConnectRequestParameters.CLIENT_ID; +import static org.mitre.openid.connect.request.ConnectRequestParameters.CODE_CHALLENGE; +import static org.mitre.openid.connect.request.ConnectRequestParameters.CODE_CHALLENGE_METHOD; import static org.mitre.openid.connect.request.ConnectRequestParameters.DISPLAY; import static org.mitre.openid.connect.request.ConnectRequestParameters.LOGIN_HINT; import static org.mitre.openid.connect.request.ConnectRequestParameters.MAX_AGE; @@ -43,7 +45,6 @@ import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.PKCEAlgorithm; import org.mitre.oauth2.service.ClientDetailsEntityService; -import org.mitre.oauth2.service.SystemScopeService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -81,9 +82,6 @@ public class ConnectOAuth2RequestFactory extends DefaultOAuth2RequestFactory { @Autowired private ClientKeyCacheService validators; - @Autowired - private SystemScopeService systemScopes; - @Autowired private JWTEncryptionAndDecryptionService encryptionService; @@ -147,7 +145,7 @@ public AuthorizationRequest createAuthorizationRequest(Map input // if the client doesn't specify a code challenge transformation method, it's "plain" request.getExtensions().put(CODE_CHALLENGE_METHOD, PKCEAlgorithm.plain.getName()); } - + } if (inputParams.containsKey(REQUEST)) { @@ -179,7 +177,7 @@ public AuthorizationRequest createAuthorizationRequest(Map input * * @param jwtString * @param request - */ + */ private void processRequestObject(String jwtString, AuthorizationRequest request) { // parse the request object diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectRequestParameters.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectRequestParameters.java index 584d09a835..6db30d320a 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectRequestParameters.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/request/ConnectRequestParameters.java @@ -43,13 +43,13 @@ public interface ConnectRequestParameters { // responses public String ERROR = "error"; public String LOGIN_REQUIRED = "login_required"; - + // audience public String AUD = "aud"; - + // PKCE public String CODE_CHALLENGE = "code_challenge"; public String CODE_CHALLENGE_METHOD = "code_challenge_method"; public String CODE_VERIFIER = "code_verifier"; - + } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultApprovedSiteService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultApprovedSiteService.java index b2bf23e1d7..e4db529d40 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultApprovedSiteService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultApprovedSiteService.java @@ -39,7 +39,7 @@ /** * Implementation of the ApprovedSiteService - * + * * @author Michael Joseph Walsh, aanganes * */ @@ -184,8 +184,8 @@ private Collection getExpired() { @Override public List getApprovedAccessTokens( ApprovedSite approvedSite) { - return tokenRepository.getAccessTokensForApprovedSite(approvedSite); + return tokenRepository.getAccessTokensForApprovedSite(approvedSite); } - + } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultBlacklistedSiteService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultBlacklistedSiteService.java index 93816056ae..38c6754afe 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultBlacklistedSiteService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultBlacklistedSiteService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.service.impl; diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultOIDCTokenService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultOIDCTokenService.java index c04e5af3df..15e764823f 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultOIDCTokenService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultOIDCTokenService.java @@ -64,7 +64,7 @@ import com.nimbusds.jwt.SignedJWT; /** * Default implementation of service to create specialty OpenID Connect tokens. - * + * * @author Amanda Anganes * */ @@ -271,12 +271,12 @@ private OAuth2AccessTokenEntity createAssociatedToken(ClientDetailsEntity client token.setAuthenticationHolder(authHolder); JWTClaimsSet claims = new JWTClaimsSet.Builder() - .audience(Lists.newArrayList(client.getClientId())) - .issuer(configBean.getIssuer()) - .issueTime(new Date()) - .expirationTime(token.getExpiration()) - .jwtID(UUID.randomUUID().toString()) // set a random NONCE in the middle of it - .build(); + .audience(Lists.newArrayList(client.getClientId())) + .issuer(configBean.getIssuer()) + .issueTime(new Date()) + .expirationTime(token.getExpiration()) + .jwtID(UUID.randomUUID().toString()) // set a random NONCE in the middle of it + .build(); JWSAlgorithm signingAlg = jwtService.getDefaultSigningAlgorithm(); JWSHeader header = new JWSHeader(signingAlg, null, null, null, null, null, null, null, null, null, diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultScopeClaimTranslationService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultScopeClaimTranslationService.java index 0dfeb86e3a..d0937cc017 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultScopeClaimTranslationService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultScopeClaimTranslationService.java @@ -17,19 +17,17 @@ package org.mitre.openid.connect.service.impl; import java.util.HashSet; -import java.util.Map; import java.util.Set; import org.mitre.openid.connect.service.ScopeClaimTranslationService; import org.springframework.stereotype.Service; import com.google.common.collect.HashMultimap; -import com.google.common.collect.Maps; import com.google.common.collect.SetMultimap; /** * Service to map scopes to claims, and claims to Java field names - * + * * @author Amanda Anganes * */ diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultStatsService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultStatsService.java index c22c1dd58d..d7897bec27 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultStatsService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultStatsService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.service.impl; @@ -26,8 +26,6 @@ import java.util.Set; import java.util.concurrent.TimeUnit; -import org.mitre.oauth2.model.ClientDetailsEntity; -import org.mitre.oauth2.service.ClientDetailsEntityService; import org.mitre.openid.connect.model.ApprovedSite; import org.mitre.openid.connect.model.ClientStat; import org.mitre.openid.connect.service.ApprovedSiteService; @@ -37,8 +35,6 @@ import com.google.common.base.Supplier; import com.google.common.base.Suppliers; -import com.google.common.collect.HashMultiset; -import com.google.common.collect.Multiset; /** * @author jricher @@ -96,10 +92,10 @@ private Map computeSummaryStats() { public ClientStat getCountForClientId(String clientId) { Collection approvedSites = approvedSiteService.getByClientId(clientId); - + ClientStat stat = new ClientStat(); stat.setApprovedSiteCount(approvedSites.size()); - + return stat; } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultUserInfoService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultUserInfoService.java index 4acc77e3b9..fbcfb8cc62 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultUserInfoService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultUserInfoService.java @@ -28,9 +28,9 @@ /** * Implementation of the UserInfoService - * + * * @author Michael Joseph Walsh, jricher - * + * */ @Service public class DefaultUserInfoService implements UserInfoService { diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultWhitelistedSiteService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultWhitelistedSiteService.java index 20611c6c3f..4b9244c0bb 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultWhitelistedSiteService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DefaultWhitelistedSiteService.java @@ -27,9 +27,9 @@ /** * Implementation of the WhitelistedSiteService - * + * * @author Michael Joseph Walsh, aanganes - * + * */ @Service @Transactional(value="defaultTransactionManager") diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DummyResourceSetService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DummyResourceSetService.java index 1b54dcab81..43aa28dc41 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DummyResourceSetService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/DummyResourceSetService.java @@ -28,7 +28,7 @@ /** * Dummy resource set service that doesn't do anything; acts as a stub for the * introspection service when the UMA functionality is disabled. - * + * * @author jricher * */ diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/InMemoryClientLogoLoadingService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/InMemoryClientLogoLoadingService.java index 428c7c51f5..3bbbaa333a 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/InMemoryClientLogoLoadingService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/InMemoryClientLogoLoadingService.java @@ -23,7 +23,6 @@ import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; -import org.apache.http.HttpException; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; @@ -31,7 +30,6 @@ import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.openid.connect.model.CachedImage; import org.mitre.openid.connect.service.ClientLogoLoadingService; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.stereotype.Service; import com.google.common.base.Strings; @@ -54,18 +52,18 @@ public InMemoryClientLogoLoadingService() { } /** - * + * */ public InMemoryClientLogoLoadingService(HttpClient httpClient) { - + cache = CacheBuilder.newBuilder() .maximumSize(100) .expireAfterAccess(14, TimeUnit.DAYS) .build(new ClientLogoFetcher(httpClient)); - + } - - + + /* (non-Javadoc) * @see org.mitre.openid.connect.service.ClientLogoLoadingService#getLogo(org.mitre.oauth2.model.ClientDetailsEntity) */ @@ -104,15 +102,15 @@ public ClientLogoFetcher(HttpClient httpClient) { public CachedImage load(ClientDetailsEntity key) throws Exception { try { HttpResponse response = httpClient.execute(new HttpGet(key.getLogoUri())); - + HttpEntity entity = response.getEntity(); - + CachedImage image = new CachedImage(); - + image.setContentType(entity.getContentType().getValue()); image.setLength(entity.getContentLength()); image.setData(IOUtils.toByteArray(entity.getContent())); - + return image; } catch (IOException e) { throw new IllegalArgumentException("Unable to load client image."); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java index c295836268..f1097e904f 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_0.java @@ -16,6 +16,9 @@ *******************************************************************************/ package org.mitre.openid.connect.service.impl; +import static org.mitre.util.JsonUtils.readMap; +import static org.mitre.util.JsonUtils.readSet; + import java.io.IOException; import java.text.ParseException; import java.util.Collection; @@ -68,9 +71,6 @@ import com.nimbusds.jose.JWEAlgorithm; import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jwt.JWTParser; - -import static org.mitre.util.JsonUtils.readMap; -import static org.mitre.util.JsonUtils.readSet; /** * * Data service to import MITREid 1.0 configuration. @@ -103,10 +103,10 @@ public class MITREidDataService_1_0 extends MITREidDataServiceSupport implements @Autowired(required = false) private List extensions = Collections.emptyList(); - private MITREidDataServiceMaps maps = new MITREidDataServiceMaps(); - + private MITREidDataServiceMaps maps = new MITREidDataServiceMaps(); + private static final String THIS_VERSION = MITREID_CONNECT_1_0; - + @Override public boolean supportsVersion(String version) { return THIS_VERSION.equals(version); @@ -135,46 +135,46 @@ public void importData(JsonReader reader) throws IOException { while (reader.hasNext()) { JsonToken tok = reader.peek(); switch (tok) { - case NAME: - String name = reader.nextName(); - // find out which member it is - if (name.equals(CLIENTS)) { - readClients(reader); - } else if (name.equals(GRANTS)) { - readGrants(reader); - } else if (name.equals(WHITELISTEDSITES)) { - readWhitelistedSites(reader); - } else if (name.equals(BLACKLISTEDSITES)) { - readBlacklistedSites(reader); - } else if (name.equals(AUTHENTICATIONHOLDERS)) { - readAuthenticationHolders(reader); - } else if (name.equals(ACCESSTOKENS)) { - readAccessTokens(reader); - } else if (name.equals(REFRESHTOKENS)) { - readRefreshTokens(reader); - } else if (name.equals(SYSTEMSCOPES)) { - readSystemScopes(reader); - } else { - for (MITREidDataServiceExtension extension : extensions) { - if (extension.supportsVersion(THIS_VERSION)) { + case NAME: + String name = reader.nextName(); + // find out which member it is + if (name.equals(CLIENTS)) { + readClients(reader); + } else if (name.equals(GRANTS)) { + readGrants(reader); + } else if (name.equals(WHITELISTEDSITES)) { + readWhitelistedSites(reader); + } else if (name.equals(BLACKLISTEDSITES)) { + readBlacklistedSites(reader); + } else if (name.equals(AUTHENTICATIONHOLDERS)) { + readAuthenticationHolders(reader); + } else if (name.equals(ACCESSTOKENS)) { + readAccessTokens(reader); + } else if (name.equals(REFRESHTOKENS)) { + readRefreshTokens(reader); + } else if (name.equals(SYSTEMSCOPES)) { + readSystemScopes(reader); + } else { + for (MITREidDataServiceExtension extension : extensions) { if (extension.supportsVersion(THIS_VERSION)) { - extension.importExtensionData(name, reader); - break; + if (extension.supportsVersion(THIS_VERSION)) { + extension.importExtensionData(name, reader); + break; + } } } + // unknown token, skip it + reader.skipValue(); } - // unknown token, skip it + break; + case END_OBJECT: + // the object ended, we're done here + reader.endObject(); + continue; + default: + logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - case END_OBJECT: - // the object ended, we're done here - reader.endObject(); - continue; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; } + continue; } } fixObjectReferences(); for (MITREidDataServiceExtension extension : extensions) { @@ -203,37 +203,37 @@ private void readRefreshTokens(JsonReader reader) throws IOException { Long authHolderId = null; while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("id")) { - currentId = reader.nextLong(); - } else if (name.equals("expiration")) { - Date date = utcToDate(reader.nextString()); - token.setExpiration(date); - } else if (name.equals("value")) { - String value = reader.nextString(); - try { - token.setJwt(JWTParser.parse(value)); - } catch (ParseException ex) { - logger.error("Unable to set refresh token value to {}", value, ex); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("id")) { + currentId = reader.nextLong(); + } else if (name.equals("expiration")) { + Date date = utcToDate(reader.nextString()); + token.setExpiration(date); + } else if (name.equals("value")) { + String value = reader.nextString(); + try { + token.setJwt(JWTParser.parse(value)); + } catch (ParseException ex) { + logger.error("Unable to set refresh token value to {}", value, ex); + } + } else if (name.equals("clientId")) { + clientId = reader.nextString(); + } else if (name.equals("authenticationHolderId")) { + authHolderId = reader.nextLong(); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - } else if (name.equals("clientId")) { - clientId = reader.nextString(); - } else if (name.equals("authenticationHolderId")) { - authHolderId = reader.nextLong(); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -265,45 +265,45 @@ private void readAccessTokens(JsonReader reader) throws IOException { Long refreshTokenId = null; while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("id")) { - currentId = reader.nextLong(); - } else if (name.equals("expiration")) { - Date date = utcToDate(reader.nextString()); - token.setExpiration(date); - } else if (name.equals("value")) { - String value = reader.nextString(); - try { - // all tokens are JWTs - token.setJwt(JWTParser.parse(value)); - } catch (ParseException ex) { - logger.error("Unable to set refresh token value to {}", value, ex); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("id")) { + currentId = reader.nextLong(); + } else if (name.equals("expiration")) { + Date date = utcToDate(reader.nextString()); + token.setExpiration(date); + } else if (name.equals("value")) { + String value = reader.nextString(); + try { + // all tokens are JWTs + token.setJwt(JWTParser.parse(value)); + } catch (ParseException ex) { + logger.error("Unable to set refresh token value to {}", value, ex); + } + } else if (name.equals("clientId")) { + clientId = reader.nextString(); + } else if (name.equals("authenticationHolderId")) { + authHolderId = reader.nextLong(); + } else if (name.equals("refreshTokenId")) { + refreshTokenId = reader.nextLong(); + } else if (name.equals("scope")) { + Set scope = readSet(reader); + token.setScope(scope); + } else if (name.equals("type")) { + token.setTokenType(reader.nextString()); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - } else if (name.equals("clientId")) { - clientId = reader.nextString(); - } else if (name.equals("authenticationHolderId")) { - authHolderId = reader.nextLong(); - } else if (name.equals("refreshTokenId")) { - refreshTokenId = reader.nextLong(); - } else if (name.equals("scope")) { - Set scope = readSet(reader); - token.setScope(scope); - } else if (name.equals("type")) { - token.setTokenType(reader.nextString()); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -331,61 +331,61 @@ private void readAuthenticationHolders(JsonReader reader) throws IOException { Long currentId = null; while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("id")) { - currentId = reader.nextLong(); - } else if (name.equals("ownerId")) { - //not needed - reader.skipValue(); - } else if (name.equals("authentication")) { - OAuth2Request clientAuthorization = null; - Authentication userAuthentication = null; - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String subName = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (subName.equals("clientAuthorization")) { - clientAuthorization = readAuthorizationRequest(reader); - } else if (subName.equals("userAuthentication")) { - // skip binary encoded version - reader.skipValue(); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("id")) { + currentId = reader.nextLong(); + } else if (name.equals("ownerId")) { + //not needed + reader.skipValue(); + } else if (name.equals("authentication")) { + OAuth2Request clientAuthorization = null; + Authentication userAuthentication = null; + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String subName = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (subName.equals("clientAuthorization")) { + clientAuthorization = readAuthorizationRequest(reader); + } else if (subName.equals("userAuthentication")) { + // skip binary encoded version + reader.skipValue(); - } else if (subName.equals("savedUserAuthentication")) { - userAuthentication = readSavedUserAuthentication(reader); + } else if (subName.equals("savedUserAuthentication")) { + userAuthentication = readSavedUserAuthentication(reader); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; } + reader.endObject(); + OAuth2Authentication auth = new OAuth2Authentication(clientAuthorization, userAuthentication); + ahe.setAuthentication(auth); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - reader.endObject(); - OAuth2Authentication auth = new OAuth2Authentication(clientAuthorization, userAuthentication); - ahe.setAuthentication(auth); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -410,47 +410,47 @@ private OAuth2Request readAuthorizationRequest(JsonReader reader) throws IOExcep reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("authorizationParameters")) { - authorizationParameters = readMap(reader); - } else if (name.equals("approvalParameters")) { - reader.skipValue(); - } else if (name.equals("clientId")) { - clientId = reader.nextString(); - } else if (name.equals("scope")) { - scope = readSet(reader); - } else if (name.equals("resourceIds")) { - resourceIds = readSet(reader); - } else if (name.equals("authorities")) { - Set authorityStrs = readSet(reader); - authorities = new HashSet<>(); - for (String s : authorityStrs) { - GrantedAuthority ga = new SimpleGrantedAuthority(s); - authorities.add(ga); - } - } else if (name.equals("approved")) { - approved = reader.nextBoolean(); - } else if (name.equals("denied")) { - if (approved == false) { - approved = !reader.nextBoolean(); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("authorizationParameters")) { + authorizationParameters = readMap(reader); + } else if (name.equals("approvalParameters")) { + reader.skipValue(); + } else if (name.equals("clientId")) { + clientId = reader.nextString(); + } else if (name.equals("scope")) { + scope = readSet(reader); + } else if (name.equals("resourceIds")) { + resourceIds = readSet(reader); + } else if (name.equals("authorities")) { + Set authorityStrs = readSet(reader); + authorities = new HashSet<>(); + for (String s : authorityStrs) { + GrantedAuthority ga = new SimpleGrantedAuthority(s); + authorities.add(ga); + } + } else if (name.equals("approved")) { + approved = reader.nextBoolean(); + } else if (name.equals("denied")) { + if (approved == false) { + approved = !reader.nextBoolean(); + } + } else if (name.equals("redirectUri")) { + redirectUri = reader.nextString(); + } else if (name.equals("responseTypes")) { + responseTypes = readSet(reader); + } else { + reader.skipValue(); } - } else if (name.equals("redirectUri")) { - redirectUri = reader.nextString(); - } else if (name.equals("responseTypes")) { - responseTypes = readSet(reader); - } else { + break; + default: + logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -468,35 +468,35 @@ private SavedUserAuthentication readSavedUserAuthentication(JsonReader reader) t while (reader.hasNext()) { switch(reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("name")) { - savedUserAuth.setName(reader.nextString()); - } else if (name.equals("sourceClass")) { - savedUserAuth.setSourceClass(reader.nextString()); - } else if (name.equals("authenticated")) { - savedUserAuth.setAuthenticated(reader.nextBoolean()); - } else if (name.equals("authorities")) { - Set authorityStrs = readSet(reader); - Set authorities = new HashSet(); - for (String s : authorityStrs) { - GrantedAuthority ga = new SimpleGrantedAuthority(s); - authorities.add(ga); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("name")) { + savedUserAuth.setName(reader.nextString()); + } else if (name.equals("sourceClass")) { + savedUserAuth.setSourceClass(reader.nextString()); + } else if (name.equals("authenticated")) { + savedUserAuth.setAuthenticated(reader.nextBoolean()); + } else if (name.equals("authorities")) { + Set authorityStrs = readSet(reader); + Set authorities = new HashSet(); + for (String s : authorityStrs) { + GrantedAuthority ga = new SimpleGrantedAuthority(s); + authorities.add(ga); + } + savedUserAuth.setAuthorities(authorities); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - savedUserAuth.setAuthorities(authorities); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } @@ -518,43 +518,43 @@ private void readGrants(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("id")) { - currentId = reader.nextLong(); - } else if (name.equals("accessDate")) { - Date date = utcToDate(reader.nextString()); - site.setAccessDate(date); - } else if (name.equals("clientId")) { - site.setClientId(reader.nextString()); - } else if (name.equals("creationDate")) { - Date date = utcToDate(reader.nextString()); - site.setCreationDate(date); - } else if (name.equals("timeoutDate")) { - Date date = utcToDate(reader.nextString()); - site.setTimeoutDate(date); - } else if (name.equals("userId")) { - site.setUserId(reader.nextString()); - } else if (name.equals("allowedScopes")) { - Set allowedScopes = readSet(reader); - site.setAllowedScopes(allowedScopes); - } else if (name.equals("whitelistedSiteId")) { - whitelistedSiteId = reader.nextLong(); - } else if (name.equals("approvedAccessTokens")) { - tokenIds = readSet(reader); - } else { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("id")) { + currentId = reader.nextLong(); + } else if (name.equals("accessDate")) { + Date date = utcToDate(reader.nextString()); + site.setAccessDate(date); + } else if (name.equals("clientId")) { + site.setClientId(reader.nextString()); + } else if (name.equals("creationDate")) { + Date date = utcToDate(reader.nextString()); + site.setCreationDate(date); + } else if (name.equals("timeoutDate")) { + Date date = utcToDate(reader.nextString()); + site.setTimeoutDate(date); + } else if (name.equals("userId")) { + site.setUserId(reader.nextString()); + } else if (name.equals("allowedScopes")) { + Set allowedScopes = readSet(reader); + site.setAllowedScopes(allowedScopes); + } else if (name.equals("whitelistedSiteId")) { + whitelistedSiteId = reader.nextLong(); + } else if (name.equals("approvedAccessTokens")) { + tokenIds = readSet(reader); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -583,28 +583,28 @@ private void readWhitelistedSites(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (name.equals("id")) { - currentId = reader.nextLong(); - } else if (name.equals("clientId")) { - wlSite.setClientId(reader.nextString()); - } else if (name.equals("creatorUserId")) { - wlSite.setCreatorUserId(reader.nextString()); - } else if (name.equals("allowedScopes")) { - Set allowedScopes = readSet(reader); - wlSite.setAllowedScopes(allowedScopes); - } else { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (name.equals("id")) { + currentId = reader.nextLong(); + } else if (name.equals("clientId")) { + wlSite.setClientId(reader.nextString()); + } else if (name.equals("creatorUserId")) { + wlSite.setCreatorUserId(reader.nextString()); + } else if (name.equals("allowedScopes")) { + Set allowedScopes = readSet(reader); + wlSite.setAllowedScopes(allowedScopes); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -626,23 +626,23 @@ private void readBlacklistedSites(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (name.equals("id")) { - reader.skipValue(); - } else if (name.equals("uri")) { - blSite.setUri(reader.nextString()); - } else { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (name.equals("id")) { + reader.skipValue(); + } else if (name.equals("uri")) { + blSite.setUri(reader.nextString()); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -663,125 +663,125 @@ private void readClients(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("clientId")) { - client.setClientId(reader.nextString()); - } else if (name.equals("resourceIds")) { - Set resourceIds = readSet(reader); - client.setResourceIds(resourceIds); - } else if (name.equals("secret")) { - client.setClientSecret(reader.nextString()); - } else if (name.equals("scope")) { - Set scope = readSet(reader); - client.setScope(scope); - } else if (name.equals("authorities")) { - Set authorityStrs = readSet(reader); - Set authorities = new HashSet<>(); - for (String s : authorityStrs) { - GrantedAuthority ga = new SimpleGrantedAuthority(s); - authorities.add(ga); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("clientId")) { + client.setClientId(reader.nextString()); + } else if (name.equals("resourceIds")) { + Set resourceIds = readSet(reader); + client.setResourceIds(resourceIds); + } else if (name.equals("secret")) { + client.setClientSecret(reader.nextString()); + } else if (name.equals("scope")) { + Set scope = readSet(reader); + client.setScope(scope); + } else if (name.equals("authorities")) { + Set authorityStrs = readSet(reader); + Set authorities = new HashSet<>(); + for (String s : authorityStrs) { + GrantedAuthority ga = new SimpleGrantedAuthority(s); + authorities.add(ga); + } + client.setAuthorities(authorities); + } else if (name.equals("accessTokenValiditySeconds")) { + client.setAccessTokenValiditySeconds(reader.nextInt()); + } else if (name.equals("refreshTokenValiditySeconds")) { + client.setRefreshTokenValiditySeconds(reader.nextInt()); + } else if (name.equals("redirectUris")) { + Set redirectUris = readSet(reader); + client.setRedirectUris(redirectUris); + } else if (name.equals("name")) { + client.setClientName(reader.nextString()); + } else if (name.equals("uri")) { + client.setClientUri(reader.nextString()); + } else if (name.equals("logoUri")) { + client.setLogoUri(reader.nextString()); + } else if (name.equals("contacts")) { + Set contacts = readSet(reader); + client.setContacts(contacts); + } else if (name.equals("tosUri")) { + client.setTosUri(reader.nextString()); + } else if (name.equals("tokenEndpointAuthMethod")) { + AuthMethod am = AuthMethod.getByValue(reader.nextString()); + client.setTokenEndpointAuthMethod(am); + } else if (name.equals("grantTypes")) { + Set grantTypes = readSet(reader); + client.setGrantTypes(grantTypes); + } else if (name.equals("responseTypes")) { + Set responseTypes = readSet(reader); + client.setResponseTypes(responseTypes); + } else if (name.equals("policyUri")) { + client.setPolicyUri(reader.nextString()); + } else if (name.equals("applicationType")) { + AppType appType = AppType.getByValue(reader.nextString()); + client.setApplicationType(appType); + } else if (name.equals("sectorIdentifierUri")) { + client.setSectorIdentifierUri(reader.nextString()); + } else if (name.equals("subjectType")) { + SubjectType st = SubjectType.getByValue(reader.nextString()); + client.setSubjectType(st); + } else if (name.equals("jwks_uri")) { + client.setJwksUri(reader.nextString()); + } else if (name.equals("requestObjectSigningAlg")) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setRequestObjectSigningAlg(alg); + } else if (name.equals("userInfoEncryptedResponseAlg")) { + JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); + client.setUserInfoEncryptedResponseAlg(alg); + } else if (name.equals("userInfoEncryptedResponseEnc")) { + EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); + client.setUserInfoEncryptedResponseEnc(alg); + } else if (name.equals("userInfoSignedResponseAlg")) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setUserInfoSignedResponseAlg(alg); + } else if (name.equals("idTokenSignedResonseAlg")) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setIdTokenSignedResponseAlg(alg); + } else if (name.equals("idTokenEncryptedResponseAlg")) { + JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); + client.setIdTokenEncryptedResponseAlg(alg); + } else if (name.equals("idTokenEncryptedResponseEnc")) { + EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); + client.setIdTokenEncryptedResponseEnc(alg); + } else if (name.equals("tokenEndpointAuthSigningAlg")) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setTokenEndpointAuthSigningAlg(alg); + } else if (name.equals("defaultMaxAge")) { + client.setDefaultMaxAge(reader.nextInt()); + } else if (name.equals("requireAuthTime")) { + client.setRequireAuthTime(reader.nextBoolean()); + } else if (name.equals("defaultACRValues")) { + Set defaultACRvalues = readSet(reader); + client.setDefaultACRvalues(defaultACRvalues); + } else if (name.equals("initiateLoginUri")) { + client.setInitiateLoginUri(reader.nextString()); + } else if (name.equals("postLogoutRedirectUri")) { + HashSet postLogoutUris = Sets.newHashSet(reader.nextString()); + client.setPostLogoutRedirectUris(postLogoutUris); + } else if (name.equals("requestUris")) { + Set requestUris = readSet(reader); + client.setRequestUris(requestUris); + } else if (name.equals("description")) { + client.setClientDescription(reader.nextString()); + } else if (name.equals("allowIntrospection")) { + client.setAllowIntrospection(reader.nextBoolean()); + } else if (name.equals("reuseRefreshToken")) { + client.setReuseRefreshToken(reader.nextBoolean()); + } else if (name.equals("dynamicallyRegistered")) { + client.setDynamicallyRegistered(reader.nextBoolean()); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - client.setAuthorities(authorities); - } else if (name.equals("accessTokenValiditySeconds")) { - client.setAccessTokenValiditySeconds(reader.nextInt()); - } else if (name.equals("refreshTokenValiditySeconds")) { - client.setRefreshTokenValiditySeconds(reader.nextInt()); - } else if (name.equals("redirectUris")) { - Set redirectUris = readSet(reader); - client.setRedirectUris(redirectUris); - } else if (name.equals("name")) { - client.setClientName(reader.nextString()); - } else if (name.equals("uri")) { - client.setClientUri(reader.nextString()); - } else if (name.equals("logoUri")) { - client.setLogoUri(reader.nextString()); - } else if (name.equals("contacts")) { - Set contacts = readSet(reader); - client.setContacts(contacts); - } else if (name.equals("tosUri")) { - client.setTosUri(reader.nextString()); - } else if (name.equals("tokenEndpointAuthMethod")) { - AuthMethod am = AuthMethod.getByValue(reader.nextString()); - client.setTokenEndpointAuthMethod(am); - } else if (name.equals("grantTypes")) { - Set grantTypes = readSet(reader); - client.setGrantTypes(grantTypes); - } else if (name.equals("responseTypes")) { - Set responseTypes = readSet(reader); - client.setResponseTypes(responseTypes); - } else if (name.equals("policyUri")) { - client.setPolicyUri(reader.nextString()); - } else if (name.equals("applicationType")) { - AppType appType = AppType.getByValue(reader.nextString()); - client.setApplicationType(appType); - } else if (name.equals("sectorIdentifierUri")) { - client.setSectorIdentifierUri(reader.nextString()); - } else if (name.equals("subjectType")) { - SubjectType st = SubjectType.getByValue(reader.nextString()); - client.setSubjectType(st); - } else if (name.equals("jwks_uri")) { - client.setJwksUri(reader.nextString()); - } else if (name.equals("requestObjectSigningAlg")) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setRequestObjectSigningAlg(alg); - } else if (name.equals("userInfoEncryptedResponseAlg")) { - JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); - client.setUserInfoEncryptedResponseAlg(alg); - } else if (name.equals("userInfoEncryptedResponseEnc")) { - EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); - client.setUserInfoEncryptedResponseEnc(alg); - } else if (name.equals("userInfoSignedResponseAlg")) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setUserInfoSignedResponseAlg(alg); - } else if (name.equals("idTokenSignedResonseAlg")) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setIdTokenSignedResponseAlg(alg); - } else if (name.equals("idTokenEncryptedResponseAlg")) { - JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); - client.setIdTokenEncryptedResponseAlg(alg); - } else if (name.equals("idTokenEncryptedResponseEnc")) { - EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); - client.setIdTokenEncryptedResponseEnc(alg); - } else if (name.equals("tokenEndpointAuthSigningAlg")) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setTokenEndpointAuthSigningAlg(alg); - } else if (name.equals("defaultMaxAge")) { - client.setDefaultMaxAge(reader.nextInt()); - } else if (name.equals("requireAuthTime")) { - client.setRequireAuthTime(reader.nextBoolean()); - } else if (name.equals("defaultACRValues")) { - Set defaultACRvalues = readSet(reader); - client.setDefaultACRvalues(defaultACRvalues); - } else if (name.equals("initiateLoginUri")) { - client.setInitiateLoginUri(reader.nextString()); - } else if (name.equals("postLogoutRedirectUri")) { - HashSet postLogoutUris = Sets.newHashSet(reader.nextString()); - client.setPostLogoutRedirectUris(postLogoutUris); - } else if (name.equals("requestUris")) { - Set requestUris = readSet(reader); - client.setRequestUris(requestUris); - } else if (name.equals("description")) { - client.setClientDescription(reader.nextString()); - } else if (name.equals("allowIntrospection")) { - client.setAllowIntrospection(reader.nextBoolean()); - } else if (name.equals("reuseRefreshToken")) { - client.setReuseRefreshToken(reader.nextBoolean()); - } else if (name.equals("dynamicallyRegistered")) { - client.setDynamicallyRegistered(reader.nextBoolean()); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -805,32 +805,32 @@ private void readSystemScopes(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("value")) { - scope.setValue(reader.nextString()); - } else if (name.equals("description")) { - scope.setDescription(reader.nextString()); - } else if (name.equals("allowDynReg")) { - // previously "allowDynReg" scopes are now tagged as "not restricted" and vice versa - scope.setRestricted(!reader.nextBoolean()); - } else if (name.equals("defaultScope")) { - scope.setDefaultScope(reader.nextBoolean()); - } else if (name.equals("icon")) { - scope.setIcon(reader.nextString()); - } else { - logger.debug("found unexpected entry"); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("value")) { + scope.setValue(reader.nextString()); + } else if (name.equals("description")) { + scope.setDescription(reader.nextString()); + } else if (name.equals("allowDynReg")) { + // previously "allowDynReg" scopes are now tagged as "not restricted" and vice versa + scope.setRestricted(!reader.nextBoolean()); + } else if (name.equals("defaultScope")) { + scope.setDefaultScope(reader.nextBoolean()); + } else if (name.equals("icon")) { + scope.setIcon(reader.nextString()); + } else { + logger.debug("found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -897,7 +897,7 @@ private void fixObjectReferences() { token.setApprovedSite(site); tokenRepository.saveAccessToken(token); } - + approvedSiteRepository.save(site); } } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java index 0edc3aea2e..2bb76b0bf5 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_1.java @@ -16,6 +16,9 @@ *******************************************************************************/ package org.mitre.openid.connect.service.impl; +import static org.mitre.util.JsonUtils.readMap; +import static org.mitre.util.JsonUtils.readSet; + import java.io.IOException; import java.io.Serializable; import java.text.ParseException; @@ -71,9 +74,6 @@ import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jwt.JWTParser; -import static org.mitre.util.JsonUtils.readMap; -import static org.mitre.util.JsonUtils.readSet; - /** * * Data service to import MITREid 1.1 configuration. @@ -107,8 +107,8 @@ public class MITREidDataService_1_1 extends MITREidDataServiceSupport implements private List extensions = Collections.emptyList(); private static final String THIS_VERSION = MITREID_CONNECT_1_1; - - private MITREidDataServiceMaps maps = new MITREidDataServiceMaps(); + + private MITREidDataServiceMaps maps = new MITREidDataServiceMaps(); @Override public boolean supportsVersion(String version) { @@ -137,46 +137,46 @@ public void importData(JsonReader reader) throws IOException { while (reader.hasNext()) { JsonToken tok = reader.peek(); switch (tok) { - case NAME: - String name = reader.nextName(); - // find out which member it is - if (name.equals(CLIENTS)) { - readClients(reader); - } else if (name.equals(GRANTS)) { - readGrants(reader); - } else if (name.equals(WHITELISTEDSITES)) { - readWhitelistedSites(reader); - } else if (name.equals(BLACKLISTEDSITES)) { - readBlacklistedSites(reader); - } else if (name.equals(AUTHENTICATIONHOLDERS)) { - readAuthenticationHolders(reader); - } else if (name.equals(ACCESSTOKENS)) { - readAccessTokens(reader); - } else if (name.equals(REFRESHTOKENS)) { - readRefreshTokens(reader); - } else if (name.equals(SYSTEMSCOPES)) { - readSystemScopes(reader); - } else { - for (MITREidDataServiceExtension extension : extensions) { - if (extension.supportsVersion(THIS_VERSION)) { + case NAME: + String name = reader.nextName(); + // find out which member it is + if (name.equals(CLIENTS)) { + readClients(reader); + } else if (name.equals(GRANTS)) { + readGrants(reader); + } else if (name.equals(WHITELISTEDSITES)) { + readWhitelistedSites(reader); + } else if (name.equals(BLACKLISTEDSITES)) { + readBlacklistedSites(reader); + } else if (name.equals(AUTHENTICATIONHOLDERS)) { + readAuthenticationHolders(reader); + } else if (name.equals(ACCESSTOKENS)) { + readAccessTokens(reader); + } else if (name.equals(REFRESHTOKENS)) { + readRefreshTokens(reader); + } else if (name.equals(SYSTEMSCOPES)) { + readSystemScopes(reader); + } else { + for (MITREidDataServiceExtension extension : extensions) { if (extension.supportsVersion(THIS_VERSION)) { - extension.importExtensionData(name, reader); - break; + if (extension.supportsVersion(THIS_VERSION)) { + extension.importExtensionData(name, reader); + break; + } } } + // unknown token, skip it + reader.skipValue(); } - // unknown token, skip it + break; + case END_OBJECT: + // the object ended, we're done here + reader.endObject(); + continue; + default: + logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - case END_OBJECT: - // the object ended, we're done here - reader.endObject(); - continue; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } fixObjectReferences(); @@ -206,37 +206,37 @@ private void readRefreshTokens(JsonReader reader) throws IOException { Long authHolderId = null; while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("id")) { - currentId = reader.nextLong(); - } else if (name.equals("expiration")) { - Date date = utcToDate(reader.nextString()); - token.setExpiration(date); - } else if (name.equals("value")) { - String value = reader.nextString(); - try { - token.setJwt(JWTParser.parse(value)); - } catch (ParseException ex) { - logger.error("Unable to set refresh token value to {}", value, ex); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("id")) { + currentId = reader.nextLong(); + } else if (name.equals("expiration")) { + Date date = utcToDate(reader.nextString()); + token.setExpiration(date); + } else if (name.equals("value")) { + String value = reader.nextString(); + try { + token.setJwt(JWTParser.parse(value)); + } catch (ParseException ex) { + logger.error("Unable to set refresh token value to {}", value, ex); + } + } else if (name.equals("clientId")) { + clientId = reader.nextString(); + } else if (name.equals("authenticationHolderId")) { + authHolderId = reader.nextLong(); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - } else if (name.equals("clientId")) { - clientId = reader.nextString(); - } else if (name.equals("authenticationHolderId")) { - authHolderId = reader.nextLong(); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -268,45 +268,45 @@ private void readAccessTokens(JsonReader reader) throws IOException { Long refreshTokenId = null; while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("id")) { - currentId = reader.nextLong(); - } else if (name.equals("expiration")) { - Date date = utcToDate(reader.nextString()); - token.setExpiration(date); - } else if (name.equals("value")) { - String value = reader.nextString(); - try { - // all tokens are JWTs - token.setJwt(JWTParser.parse(value)); - } catch (ParseException ex) { - logger.error("Unable to set refresh token value to {}", value, ex); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("id")) { + currentId = reader.nextLong(); + } else if (name.equals("expiration")) { + Date date = utcToDate(reader.nextString()); + token.setExpiration(date); + } else if (name.equals("value")) { + String value = reader.nextString(); + try { + // all tokens are JWTs + token.setJwt(JWTParser.parse(value)); + } catch (ParseException ex) { + logger.error("Unable to set refresh token value to {}", value, ex); + } + } else if (name.equals("clientId")) { + clientId = reader.nextString(); + } else if (name.equals("authenticationHolderId")) { + authHolderId = reader.nextLong(); + } else if (name.equals("refreshTokenId")) { + refreshTokenId = reader.nextLong(); + } else if (name.equals("scope")) { + Set scope = readSet(reader); + token.setScope(scope); + } else if (name.equals("type")) { + token.setTokenType(reader.nextString()); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - } else if (name.equals("clientId")) { - clientId = reader.nextString(); - } else if (name.equals("authenticationHolderId")) { - authHolderId = reader.nextLong(); - } else if (name.equals("refreshTokenId")) { - refreshTokenId = reader.nextLong(); - } else if (name.equals("scope")) { - Set scope = readSet(reader); - token.setScope(scope); - } else if (name.equals("type")) { - token.setTokenType(reader.nextString()); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -334,61 +334,61 @@ private void readAuthenticationHolders(JsonReader reader) throws IOException { Long currentId = null; while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("id")) { - currentId = reader.nextLong(); - } else if (name.equals("ownerId")) { - //not needed - reader.skipValue(); - } else if (name.equals("authentication")) { - OAuth2Request clientAuthorization = null; - Authentication userAuthentication = null; - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String subName = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); // skip null values - } else if (subName.equals("clientAuthorization")) { - clientAuthorization = readAuthorizationRequest(reader); - } else if (subName.equals("userAuthentication")) { - // skip binary encoded version - reader.skipValue(); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("id")) { + currentId = reader.nextLong(); + } else if (name.equals("ownerId")) { + //not needed + reader.skipValue(); + } else if (name.equals("authentication")) { + OAuth2Request clientAuthorization = null; + Authentication userAuthentication = null; + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String subName = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); // skip null values + } else if (subName.equals("clientAuthorization")) { + clientAuthorization = readAuthorizationRequest(reader); + } else if (subName.equals("userAuthentication")) { + // skip binary encoded version + reader.skipValue(); - } else if (subName.equals("savedUserAuthentication")) { - userAuthentication = readSavedUserAuthentication(reader); + } else if (subName.equals("savedUserAuthentication")) { + userAuthentication = readSavedUserAuthentication(reader); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; } + reader.endObject(); + OAuth2Authentication auth = new OAuth2Authentication(clientAuthorization, userAuthentication); + ahe.setAuthentication(auth); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - reader.endObject(); - OAuth2Authentication auth = new OAuth2Authentication(clientAuthorization, userAuthentication); - ahe.setAuthentication(auth); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -414,53 +414,53 @@ private OAuth2Request readAuthorizationRequest(JsonReader reader) throws IOExcep reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("requestParameters")) { - requestParameters = readMap(reader); - } else if (name.equals("clientId")) { - clientId = reader.nextString(); - } else if (name.equals("scope")) { - scope = readSet(reader); - } else if (name.equals("resourceIds")) { - resourceIds = readSet(reader); - } else if (name.equals("authorities")) { - Set authorityStrs = readSet(reader); - authorities = new HashSet<>(); - for (String s : authorityStrs) { - GrantedAuthority ga = new SimpleGrantedAuthority(s); - authorities.add(ga); - } - } else if (name.equals("approved")) { - approved = reader.nextBoolean(); - } else if (name.equals("denied")) { - if (approved == false) { - approved = !reader.nextBoolean(); - } - } else if (name.equals("redirectUri")) { - redirectUri = reader.nextString(); - } else if (name.equals("responseTypes")) { - responseTypes = readSet(reader); - } else if (name.equals("extensions")) { - // skip the binary encoded version - reader.skipValue(); - } else if (name.equals("extensionStrings")) { - Map extEnc = readMap(reader); - for (Entry entry : extEnc.entrySet()) { - extensions.put(entry.getKey(), entry.getValue()); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("requestParameters")) { + requestParameters = readMap(reader); + } else if (name.equals("clientId")) { + clientId = reader.nextString(); + } else if (name.equals("scope")) { + scope = readSet(reader); + } else if (name.equals("resourceIds")) { + resourceIds = readSet(reader); + } else if (name.equals("authorities")) { + Set authorityStrs = readSet(reader); + authorities = new HashSet<>(); + for (String s : authorityStrs) { + GrantedAuthority ga = new SimpleGrantedAuthority(s); + authorities.add(ga); + } + } else if (name.equals("approved")) { + approved = reader.nextBoolean(); + } else if (name.equals("denied")) { + if (approved == false) { + approved = !reader.nextBoolean(); + } + } else if (name.equals("redirectUri")) { + redirectUri = reader.nextString(); + } else if (name.equals("responseTypes")) { + responseTypes = readSet(reader); + } else if (name.equals("extensions")) { + // skip the binary encoded version + reader.skipValue(); + } else if (name.equals("extensionStrings")) { + Map extEnc = readMap(reader); + for (Entry entry : extEnc.entrySet()) { + extensions.put(entry.getKey(), entry.getValue()); + } + } else { + reader.skipValue(); } - } else { + break; + default: + logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -478,35 +478,35 @@ private SavedUserAuthentication readSavedUserAuthentication(JsonReader reader) t while (reader.hasNext()) { switch(reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("name")) { - savedUserAuth.setName(reader.nextString()); - } else if (name.equals("sourceClass")) { - savedUserAuth.setSourceClass(reader.nextString()); - } else if (name.equals("authenticated")) { - savedUserAuth.setAuthenticated(reader.nextBoolean()); - } else if (name.equals("authorities")) { - Set authorityStrs = readSet(reader); - Set authorities = new HashSet(); - for (String s : authorityStrs) { - GrantedAuthority ga = new SimpleGrantedAuthority(s); - authorities.add(ga); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("name")) { + savedUserAuth.setName(reader.nextString()); + } else if (name.equals("sourceClass")) { + savedUserAuth.setSourceClass(reader.nextString()); + } else if (name.equals("authenticated")) { + savedUserAuth.setAuthenticated(reader.nextBoolean()); + } else if (name.equals("authorities")) { + Set authorityStrs = readSet(reader); + Set authorities = new HashSet(); + for (String s : authorityStrs) { + GrantedAuthority ga = new SimpleGrantedAuthority(s); + authorities.add(ga); + } + savedUserAuth.setAuthorities(authorities); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - savedUserAuth.setAuthorities(authorities); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } @@ -528,43 +528,43 @@ private void readGrants(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("id")) { - currentId = reader.nextLong(); - } else if (name.equals("accessDate")) { - Date date = utcToDate(reader.nextString()); - site.setAccessDate(date); - } else if (name.equals("clientId")) { - site.setClientId(reader.nextString()); - } else if (name.equals("creationDate")) { - Date date = utcToDate(reader.nextString()); - site.setCreationDate(date); - } else if (name.equals("timeoutDate")) { - Date date = utcToDate(reader.nextString()); - site.setTimeoutDate(date); - } else if (name.equals("userId")) { - site.setUserId(reader.nextString()); - } else if (name.equals("allowedScopes")) { - Set allowedScopes = readSet(reader); - site.setAllowedScopes(allowedScopes); - } else if (name.equals("whitelistedSiteId")) { - whitelistedSiteId = reader.nextLong(); - } else if (name.equals("approvedAccessTokens")) { - tokenIds = readSet(reader); - } else { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("id")) { + currentId = reader.nextLong(); + } else if (name.equals("accessDate")) { + Date date = utcToDate(reader.nextString()); + site.setAccessDate(date); + } else if (name.equals("clientId")) { + site.setClientId(reader.nextString()); + } else if (name.equals("creationDate")) { + Date date = utcToDate(reader.nextString()); + site.setCreationDate(date); + } else if (name.equals("timeoutDate")) { + Date date = utcToDate(reader.nextString()); + site.setTimeoutDate(date); + } else if (name.equals("userId")) { + site.setUserId(reader.nextString()); + } else if (name.equals("allowedScopes")) { + Set allowedScopes = readSet(reader); + site.setAllowedScopes(allowedScopes); + } else if (name.equals("whitelistedSiteId")) { + whitelistedSiteId = reader.nextLong(); + } else if (name.equals("approvedAccessTokens")) { + tokenIds = readSet(reader); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -593,28 +593,28 @@ private void readWhitelistedSites(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (name.equals("id")) { - currentId = reader.nextLong(); - } else if (name.equals("clientId")) { - wlSite.setClientId(reader.nextString()); - } else if (name.equals("creatorUserId")) { - wlSite.setCreatorUserId(reader.nextString()); - } else if (name.equals("allowedScopes")) { - Set allowedScopes = readSet(reader); - wlSite.setAllowedScopes(allowedScopes); - } else { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (name.equals("id")) { + currentId = reader.nextLong(); + } else if (name.equals("clientId")) { + wlSite.setClientId(reader.nextString()); + } else if (name.equals("creatorUserId")) { + wlSite.setCreatorUserId(reader.nextString()); + } else if (name.equals("allowedScopes")) { + Set allowedScopes = readSet(reader); + wlSite.setAllowedScopes(allowedScopes); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -636,23 +636,23 @@ private void readBlacklistedSites(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (name.equals("id")) { - reader.skipValue(); - } else if (name.equals("uri")) { - blSite.setUri(reader.nextString()); - } else { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (name.equals("id")) { + reader.skipValue(); + } else if (name.equals("uri")) { + blSite.setUri(reader.nextString()); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -673,125 +673,125 @@ private void readClients(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("clientId")) { - client.setClientId(reader.nextString()); - } else if (name.equals("resourceIds")) { - Set resourceIds = readSet(reader); - client.setResourceIds(resourceIds); - } else if (name.equals("secret")) { - client.setClientSecret(reader.nextString()); - } else if (name.equals("scope")) { - Set scope = readSet(reader); - client.setScope(scope); - } else if (name.equals("authorities")) { - Set authorityStrs = readSet(reader); - Set authorities = new HashSet<>(); - for (String s : authorityStrs) { - GrantedAuthority ga = new SimpleGrantedAuthority(s); - authorities.add(ga); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("clientId")) { + client.setClientId(reader.nextString()); + } else if (name.equals("resourceIds")) { + Set resourceIds = readSet(reader); + client.setResourceIds(resourceIds); + } else if (name.equals("secret")) { + client.setClientSecret(reader.nextString()); + } else if (name.equals("scope")) { + Set scope = readSet(reader); + client.setScope(scope); + } else if (name.equals("authorities")) { + Set authorityStrs = readSet(reader); + Set authorities = new HashSet<>(); + for (String s : authorityStrs) { + GrantedAuthority ga = new SimpleGrantedAuthority(s); + authorities.add(ga); + } + client.setAuthorities(authorities); + } else if (name.equals("accessTokenValiditySeconds")) { + client.setAccessTokenValiditySeconds(reader.nextInt()); + } else if (name.equals("refreshTokenValiditySeconds")) { + client.setRefreshTokenValiditySeconds(reader.nextInt()); + } else if (name.equals("redirectUris")) { + Set redirectUris = readSet(reader); + client.setRedirectUris(redirectUris); + } else if (name.equals("name")) { + client.setClientName(reader.nextString()); + } else if (name.equals("uri")) { + client.setClientUri(reader.nextString()); + } else if (name.equals("logoUri")) { + client.setLogoUri(reader.nextString()); + } else if (name.equals("contacts")) { + Set contacts = readSet(reader); + client.setContacts(contacts); + } else if (name.equals("tosUri")) { + client.setTosUri(reader.nextString()); + } else if (name.equals("tokenEndpointAuthMethod")) { + AuthMethod am = AuthMethod.getByValue(reader.nextString()); + client.setTokenEndpointAuthMethod(am); + } else if (name.equals("grantTypes")) { + Set grantTypes = readSet(reader); + client.setGrantTypes(grantTypes); + } else if (name.equals("responseTypes")) { + Set responseTypes = readSet(reader); + client.setResponseTypes(responseTypes); + } else if (name.equals("policyUri")) { + client.setPolicyUri(reader.nextString()); + } else if (name.equals("applicationType")) { + AppType appType = AppType.getByValue(reader.nextString()); + client.setApplicationType(appType); + } else if (name.equals("sectorIdentifierUri")) { + client.setSectorIdentifierUri(reader.nextString()); + } else if (name.equals("subjectType")) { + SubjectType st = SubjectType.getByValue(reader.nextString()); + client.setSubjectType(st); + } else if (name.equals("jwks_uri")) { + client.setJwksUri(reader.nextString()); + } else if (name.equals("requestObjectSigningAlg")) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setRequestObjectSigningAlg(alg); + } else if (name.equals("userInfoEncryptedResponseAlg")) { + JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); + client.setUserInfoEncryptedResponseAlg(alg); + } else if (name.equals("userInfoEncryptedResponseEnc")) { + EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); + client.setUserInfoEncryptedResponseEnc(alg); + } else if (name.equals("userInfoSignedResponseAlg")) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setUserInfoSignedResponseAlg(alg); + } else if (name.equals("idTokenSignedResonseAlg")) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setIdTokenSignedResponseAlg(alg); + } else if (name.equals("idTokenEncryptedResponseAlg")) { + JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); + client.setIdTokenEncryptedResponseAlg(alg); + } else if (name.equals("idTokenEncryptedResponseEnc")) { + EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); + client.setIdTokenEncryptedResponseEnc(alg); + } else if (name.equals("tokenEndpointAuthSigningAlg")) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setTokenEndpointAuthSigningAlg(alg); + } else if (name.equals("defaultMaxAge")) { + client.setDefaultMaxAge(reader.nextInt()); + } else if (name.equals("requireAuthTime")) { + client.setRequireAuthTime(reader.nextBoolean()); + } else if (name.equals("defaultACRValues")) { + Set defaultACRvalues = readSet(reader); + client.setDefaultACRvalues(defaultACRvalues); + } else if (name.equals("initiateLoginUri")) { + client.setInitiateLoginUri(reader.nextString()); + } else if (name.equals("postLogoutRedirectUri")) { + HashSet postLogoutUris = Sets.newHashSet(reader.nextString()); + client.setPostLogoutRedirectUris(postLogoutUris); + } else if (name.equals("requestUris")) { + Set requestUris = readSet(reader); + client.setRequestUris(requestUris); + } else if (name.equals("description")) { + client.setClientDescription(reader.nextString()); + } else if (name.equals("allowIntrospection")) { + client.setAllowIntrospection(reader.nextBoolean()); + } else if (name.equals("reuseRefreshToken")) { + client.setReuseRefreshToken(reader.nextBoolean()); + } else if (name.equals("dynamicallyRegistered")) { + client.setDynamicallyRegistered(reader.nextBoolean()); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - client.setAuthorities(authorities); - } else if (name.equals("accessTokenValiditySeconds")) { - client.setAccessTokenValiditySeconds(reader.nextInt()); - } else if (name.equals("refreshTokenValiditySeconds")) { - client.setRefreshTokenValiditySeconds(reader.nextInt()); - } else if (name.equals("redirectUris")) { - Set redirectUris = readSet(reader); - client.setRedirectUris(redirectUris); - } else if (name.equals("name")) { - client.setClientName(reader.nextString()); - } else if (name.equals("uri")) { - client.setClientUri(reader.nextString()); - } else if (name.equals("logoUri")) { - client.setLogoUri(reader.nextString()); - } else if (name.equals("contacts")) { - Set contacts = readSet(reader); - client.setContacts(contacts); - } else if (name.equals("tosUri")) { - client.setTosUri(reader.nextString()); - } else if (name.equals("tokenEndpointAuthMethod")) { - AuthMethod am = AuthMethod.getByValue(reader.nextString()); - client.setTokenEndpointAuthMethod(am); - } else if (name.equals("grantTypes")) { - Set grantTypes = readSet(reader); - client.setGrantTypes(grantTypes); - } else if (name.equals("responseTypes")) { - Set responseTypes = readSet(reader); - client.setResponseTypes(responseTypes); - } else if (name.equals("policyUri")) { - client.setPolicyUri(reader.nextString()); - } else if (name.equals("applicationType")) { - AppType appType = AppType.getByValue(reader.nextString()); - client.setApplicationType(appType); - } else if (name.equals("sectorIdentifierUri")) { - client.setSectorIdentifierUri(reader.nextString()); - } else if (name.equals("subjectType")) { - SubjectType st = SubjectType.getByValue(reader.nextString()); - client.setSubjectType(st); - } else if (name.equals("jwks_uri")) { - client.setJwksUri(reader.nextString()); - } else if (name.equals("requestObjectSigningAlg")) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setRequestObjectSigningAlg(alg); - } else if (name.equals("userInfoEncryptedResponseAlg")) { - JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); - client.setUserInfoEncryptedResponseAlg(alg); - } else if (name.equals("userInfoEncryptedResponseEnc")) { - EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); - client.setUserInfoEncryptedResponseEnc(alg); - } else if (name.equals("userInfoSignedResponseAlg")) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setUserInfoSignedResponseAlg(alg); - } else if (name.equals("idTokenSignedResonseAlg")) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setIdTokenSignedResponseAlg(alg); - } else if (name.equals("idTokenEncryptedResponseAlg")) { - JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); - client.setIdTokenEncryptedResponseAlg(alg); - } else if (name.equals("idTokenEncryptedResponseEnc")) { - EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); - client.setIdTokenEncryptedResponseEnc(alg); - } else if (name.equals("tokenEndpointAuthSigningAlg")) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setTokenEndpointAuthSigningAlg(alg); - } else if (name.equals("defaultMaxAge")) { - client.setDefaultMaxAge(reader.nextInt()); - } else if (name.equals("requireAuthTime")) { - client.setRequireAuthTime(reader.nextBoolean()); - } else if (name.equals("defaultACRValues")) { - Set defaultACRvalues = readSet(reader); - client.setDefaultACRvalues(defaultACRvalues); - } else if (name.equals("initiateLoginUri")) { - client.setInitiateLoginUri(reader.nextString()); - } else if (name.equals("postLogoutRedirectUri")) { - HashSet postLogoutUris = Sets.newHashSet(reader.nextString()); - client.setPostLogoutRedirectUris(postLogoutUris); - } else if (name.equals("requestUris")) { - Set requestUris = readSet(reader); - client.setRequestUris(requestUris); - } else if (name.equals("description")) { - client.setClientDescription(reader.nextString()); - } else if (name.equals("allowIntrospection")) { - client.setAllowIntrospection(reader.nextBoolean()); - } else if (name.equals("reuseRefreshToken")) { - client.setReuseRefreshToken(reader.nextBoolean()); - } else if (name.equals("dynamicallyRegistered")) { - client.setDynamicallyRegistered(reader.nextBoolean()); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -815,36 +815,36 @@ private void readSystemScopes(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals("value")) { - scope.setValue(reader.nextString()); - } else if (name.equals("description")) { - scope.setDescription(reader.nextString()); - } else if (name.equals("allowDynReg")) { - // previously "allowDynReg" scopes are now tagged as "not restricted" and vice versa - scope.setRestricted(!reader.nextBoolean()); - } else if (name.equals("defaultScope")) { - scope.setDefaultScope(reader.nextBoolean()); - } else if (name.equals("structured")) { - logger.warn("Found a structured scope, ignoring structure"); - } else if (name.equals("structuredParameter")) { - logger.warn("Found a structured scope, ignoring structure"); - } else if (name.equals("icon")) { - scope.setIcon(reader.nextString()); - } else { - logger.debug("found unexpected entry"); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals("value")) { + scope.setValue(reader.nextString()); + } else if (name.equals("description")) { + scope.setDescription(reader.nextString()); + } else if (name.equals("allowDynReg")) { + // previously "allowDynReg" scopes are now tagged as "not restricted" and vice versa + scope.setRestricted(!reader.nextBoolean()); + } else if (name.equals("defaultScope")) { + scope.setDefaultScope(reader.nextBoolean()); + } else if (name.equals("structured")) { + logger.warn("Found a structured scope, ignoring structure"); + } else if (name.equals("structuredParameter")) { + logger.warn("Found a structured scope, ignoring structure"); + } else if (name.equals("icon")) { + scope.setIcon(reader.nextString()); + } else { + logger.debug("found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -911,7 +911,7 @@ private void fixObjectReferences() { token.setApprovedSite(site); tokenRepository.saveAccessToken(token); } - + approvedSiteRepository.save(site); } } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java index b5f184c3b0..aeffbfa39f 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java @@ -16,16 +16,15 @@ *******************************************************************************/ package org.mitre.openid.connect.service.impl; +import static org.mitre.util.JsonUtils.readMap; +import static org.mitre.util.JsonUtils.readSet; + import java.io.IOException; -import java.io.Serializable; import java.text.ParseException; import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import org.mitre.oauth2.model.AuthenticationHolderEntity; @@ -66,10 +65,6 @@ import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jwt.JWTParser; -import static org.mitre.util.JsonUtils.readMap; -import static org.mitre.util.JsonUtils.readSet; -import static org.mitre.util.JsonUtils.writeNullSafeArray; - /** * * Data service to import and export MITREid 1.2 configuration. @@ -169,10 +164,10 @@ public class MITREidDataService_1_2 extends MITREidDataServiceSupport implements @Autowired(required = false) private List extensions = Collections.emptyList(); - private MITREidDataServiceMaps maps = new MITREidDataServiceMaps(); + private MITREidDataServiceMaps maps = new MITREidDataServiceMaps(); private static final String THIS_VERSION = MITREID_CONNECT_1_2; - + @Override public boolean supportsVersion(String version) { return THIS_VERSION.equals(version); @@ -201,44 +196,44 @@ public void importData(JsonReader reader) throws IOException { while (reader.hasNext()) { JsonToken tok = reader.peek(); switch (tok) { - case NAME: - String name = reader.nextName(); - // find out which member it is - if (name.equals(CLIENTS)) { - readClients(reader); - } else if (name.equals(GRANTS)) { - readGrants(reader); - } else if (name.equals(WHITELISTEDSITES)) { - readWhitelistedSites(reader); - } else if (name.equals(BLACKLISTEDSITES)) { - readBlacklistedSites(reader); - } else if (name.equals(AUTHENTICATIONHOLDERS)) { - readAuthenticationHolders(reader); - } else if (name.equals(ACCESSTOKENS)) { - readAccessTokens(reader); - } else if (name.equals(REFRESHTOKENS)) { - readRefreshTokens(reader); - } else if (name.equals(SYSTEMSCOPES)) { - readSystemScopes(reader); - } else { - for (MITREidDataServiceExtension extension : extensions) { - if (extension.supportsVersion(THIS_VERSION)) { - extension.importExtensionData(name, reader); - break; + case NAME: + String name = reader.nextName(); + // find out which member it is + if (name.equals(CLIENTS)) { + readClients(reader); + } else if (name.equals(GRANTS)) { + readGrants(reader); + } else if (name.equals(WHITELISTEDSITES)) { + readWhitelistedSites(reader); + } else if (name.equals(BLACKLISTEDSITES)) { + readBlacklistedSites(reader); + } else if (name.equals(AUTHENTICATIONHOLDERS)) { + readAuthenticationHolders(reader); + } else if (name.equals(ACCESSTOKENS)) { + readAccessTokens(reader); + } else if (name.equals(REFRESHTOKENS)) { + readRefreshTokens(reader); + } else if (name.equals(SYSTEMSCOPES)) { + readSystemScopes(reader); + } else { + for (MITREidDataServiceExtension extension : extensions) { + if (extension.supportsVersion(THIS_VERSION)) { + extension.importExtensionData(name, reader); + break; + } } + // unknown token, skip it + reader.skipValue(); } - // unknown token, skip it + break; + case END_OBJECT: + // the object ended, we're done here + reader.endObject(); + continue; + default: + logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - case END_OBJECT: - // the object ended, we're done here - reader.endObject(); - continue; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } fixObjectReferences(); @@ -268,37 +263,37 @@ private void readRefreshTokens(JsonReader reader) throws IOException { Long authHolderId = null; while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ID)) { - currentId = reader.nextLong(); - } else if (name.equals(EXPIRATION)) { - Date date = utcToDate(reader.nextString()); - token.setExpiration(date); - } else if (name.equals(VALUE)) { - String value = reader.nextString(); - try { - token.setJwt(JWTParser.parse(value)); - } catch (ParseException ex) { - logger.error("Unable to set refresh token value to {}", value, ex); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ID)) { + currentId = reader.nextLong(); + } else if (name.equals(EXPIRATION)) { + Date date = utcToDate(reader.nextString()); + token.setExpiration(date); + } else if (name.equals(VALUE)) { + String value = reader.nextString(); + try { + token.setJwt(JWTParser.parse(value)); + } catch (ParseException ex) { + logger.error("Unable to set refresh token value to {}", value, ex); + } + } else if (name.equals(CLIENT_ID)) { + clientId = reader.nextString(); + } else if (name.equals(AUTHENTICATION_HOLDER_ID)) { + authHolderId = reader.nextLong(); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - } else if (name.equals(CLIENT_ID)) { - clientId = reader.nextString(); - } else if (name.equals(AUTHENTICATION_HOLDER_ID)) { - authHolderId = reader.nextLong(); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -330,45 +325,45 @@ private void readAccessTokens(JsonReader reader) throws IOException { Long refreshTokenId = null; while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ID)) { - currentId = reader.nextLong(); - } else if (name.equals(EXPIRATION)) { - Date date = utcToDate(reader.nextString()); - token.setExpiration(date); - } else if (name.equals(VALUE)) { - String value = reader.nextString(); - try { - // all tokens are JWTs - token.setJwt(JWTParser.parse(value)); - } catch (ParseException ex) { - logger.error("Unable to set refresh token value to {}", value, ex); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ID)) { + currentId = reader.nextLong(); + } else if (name.equals(EXPIRATION)) { + Date date = utcToDate(reader.nextString()); + token.setExpiration(date); + } else if (name.equals(VALUE)) { + String value = reader.nextString(); + try { + // all tokens are JWTs + token.setJwt(JWTParser.parse(value)); + } catch (ParseException ex) { + logger.error("Unable to set refresh token value to {}", value, ex); + } + } else if (name.equals(CLIENT_ID)) { + clientId = reader.nextString(); + } else if (name.equals(AUTHENTICATION_HOLDER_ID)) { + authHolderId = reader.nextLong(); + } else if (name.equals(REFRESH_TOKEN_ID)) { + refreshTokenId = reader.nextLong(); + } else if (name.equals(SCOPE)) { + Set scope = readSet(reader); + token.setScope(scope); + } else if (name.equals(TYPE)) { + token.setTokenType(reader.nextString()); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - } else if (name.equals(CLIENT_ID)) { - clientId = reader.nextString(); - } else if (name.equals(AUTHENTICATION_HOLDER_ID)) { - authHolderId = reader.nextLong(); - } else if (name.equals(REFRESH_TOKEN_ID)) { - refreshTokenId = reader.nextLong(); - } else if (name.equals(SCOPE)) { - Set scope = readSet(reader); - token.setScope(scope); - } else if (name.equals(TYPE)) { - token.setTokenType(reader.nextString()); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -396,49 +391,49 @@ private void readAuthenticationHolders(JsonReader reader) throws IOException { Long currentId = null; while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ID)) { - currentId = reader.nextLong(); - } else if (name.equals(REQUEST_PARAMETERS)) { - ahe.setRequestParameters(readMap(reader)); - } else if (name.equals(CLIENT_ID)) { - ahe.setClientId(reader.nextString()); - } else if (name.equals(SCOPE)) { - ahe.setScope(readSet(reader)); - } else if (name.equals(RESOURCE_IDS)) { - ahe.setResourceIds(readSet(reader)); - } else if (name.equals(AUTHORITIES)) { - Set authorityStrs = readSet(reader); - Set authorities = new HashSet(); - for (String s : authorityStrs) { - GrantedAuthority ga = new SimpleGrantedAuthority(s); - authorities.add(ga); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ID)) { + currentId = reader.nextLong(); + } else if (name.equals(REQUEST_PARAMETERS)) { + ahe.setRequestParameters(readMap(reader)); + } else if (name.equals(CLIENT_ID)) { + ahe.setClientId(reader.nextString()); + } else if (name.equals(SCOPE)) { + ahe.setScope(readSet(reader)); + } else if (name.equals(RESOURCE_IDS)) { + ahe.setResourceIds(readSet(reader)); + } else if (name.equals(AUTHORITIES)) { + Set authorityStrs = readSet(reader); + Set authorities = new HashSet(); + for (String s : authorityStrs) { + GrantedAuthority ga = new SimpleGrantedAuthority(s); + authorities.add(ga); + } + ahe.setAuthorities(authorities); + } else if (name.equals(APPROVED)) { + ahe.setApproved(reader.nextBoolean()); + } else if (name.equals(REDIRECT_URI)) { + ahe.setRedirectUri(reader.nextString()); + } else if (name.equals(RESPONSE_TYPES)) { + ahe.setResponseTypes(readSet(reader)); + } else if (name.equals(EXTENSIONS)) { + ahe.setExtensions(readMap(reader)); + } else if (name.equals(SAVED_USER_AUTHENTICATION)) { + ahe.setUserAuth(readSavedUserAuthentication(reader)); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - ahe.setAuthorities(authorities); - } else if (name.equals(APPROVED)) { - ahe.setApproved(reader.nextBoolean()); - } else if (name.equals(REDIRECT_URI)) { - ahe.setRedirectUri(reader.nextString()); - } else if (name.equals(RESPONSE_TYPES)) { - ahe.setResponseTypes(readSet(reader)); - } else if (name.equals(EXTENSIONS)) { - ahe.setExtensions(readMap(reader)); - } else if (name.equals(SAVED_USER_AUTHENTICATION)) { - ahe.setUserAuth(readSavedUserAuthentication(reader)); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -461,35 +456,35 @@ private SavedUserAuthentication readSavedUserAuthentication(JsonReader reader) t while (reader.hasNext()) { switch(reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(NAME)) { - savedUserAuth.setName(reader.nextString()); - } else if (name.equals(SOURCE_CLASS)) { - savedUserAuth.setSourceClass(reader.nextString()); - } else if (name.equals(AUTHENTICATED)) { - savedUserAuth.setAuthenticated(reader.nextBoolean()); - } else if (name.equals(AUTHORITIES)) { - Set authorityStrs = readSet(reader); - Set authorities = new HashSet(); - for (String s : authorityStrs) { - GrantedAuthority ga = new SimpleGrantedAuthority(s); - authorities.add(ga); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(NAME)) { + savedUserAuth.setName(reader.nextString()); + } else if (name.equals(SOURCE_CLASS)) { + savedUserAuth.setSourceClass(reader.nextString()); + } else if (name.equals(AUTHENTICATED)) { + savedUserAuth.setAuthenticated(reader.nextBoolean()); + } else if (name.equals(AUTHORITIES)) { + Set authorityStrs = readSet(reader); + Set authorities = new HashSet(); + for (String s : authorityStrs) { + GrantedAuthority ga = new SimpleGrantedAuthority(s); + authorities.add(ga); + } + savedUserAuth.setAuthorities(authorities); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - savedUserAuth.setAuthorities(authorities); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } @@ -510,41 +505,41 @@ private void readGrants(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ID)) { - currentId = reader.nextLong(); - } else if (name.equals(ACCESS_DATE)) { - Date date = utcToDate(reader.nextString()); - site.setAccessDate(date); - } else if (name.equals(CLIENT_ID)) { - site.setClientId(reader.nextString()); - } else if (name.equals(CREATION_DATE)) { - Date date = utcToDate(reader.nextString()); - site.setCreationDate(date); - } else if (name.equals(TIMEOUT_DATE)) { - Date date = utcToDate(reader.nextString()); - site.setTimeoutDate(date); - } else if (name.equals(USER_ID)) { - site.setUserId(reader.nextString()); - } else if (name.equals(ALLOWED_SCOPES)) { - Set allowedScopes = readSet(reader); - site.setAllowedScopes(allowedScopes); - } else if (name.equals(APPROVED_ACCESS_TOKENS)) { - tokenIds = readSet(reader); - } else { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ID)) { + currentId = reader.nextLong(); + } else if (name.equals(ACCESS_DATE)) { + Date date = utcToDate(reader.nextString()); + site.setAccessDate(date); + } else if (name.equals(CLIENT_ID)) { + site.setClientId(reader.nextString()); + } else if (name.equals(CREATION_DATE)) { + Date date = utcToDate(reader.nextString()); + site.setCreationDate(date); + } else if (name.equals(TIMEOUT_DATE)) { + Date date = utcToDate(reader.nextString()); + site.setTimeoutDate(date); + } else if (name.equals(USER_ID)) { + site.setUserId(reader.nextString()); + } else if (name.equals(ALLOWED_SCOPES)) { + Set allowedScopes = readSet(reader); + site.setAllowedScopes(allowedScopes); + } else if (name.equals(APPROVED_ACCESS_TOKENS)) { + tokenIds = readSet(reader); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -570,28 +565,28 @@ private void readWhitelistedSites(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (name.equals(ID)) { - currentId = reader.nextLong(); - } else if (name.equals(CLIENT_ID)) { - wlSite.setClientId(reader.nextString()); - } else if (name.equals(CREATOR_USER_ID)) { - wlSite.setCreatorUserId(reader.nextString()); - } else if (name.equals(ALLOWED_SCOPES)) { - Set allowedScopes = readSet(reader); - wlSite.setAllowedScopes(allowedScopes); - } else { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (name.equals(ID)) { + currentId = reader.nextLong(); + } else if (name.equals(CLIENT_ID)) { + wlSite.setClientId(reader.nextString()); + } else if (name.equals(CREATOR_USER_ID)) { + wlSite.setCreatorUserId(reader.nextString()); + } else if (name.equals(ALLOWED_SCOPES)) { + Set allowedScopes = readSet(reader); + wlSite.setAllowedScopes(allowedScopes); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -613,23 +608,23 @@ private void readBlacklistedSites(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (name.equals(ID)) { - reader.skipValue(); - } else if (name.equals(URI)) { - blSite.setUri(reader.nextString()); - } else { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (name.equals(ID)) { + reader.skipValue(); + } else if (name.equals(URI)) { + blSite.setUri(reader.nextString()); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -650,136 +645,136 @@ private void readClients(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(CLIENT_ID)) { - client.setClientId(reader.nextString()); - } else if (name.equals(RESOURCE_IDS)) { - Set resourceIds = readSet(reader); - client.setResourceIds(resourceIds); - } else if (name.equals(SECRET)) { - client.setClientSecret(reader.nextString()); - } else if (name.equals(SCOPE)) { - Set scope = readSet(reader); - client.setScope(scope); - } else if (name.equals(AUTHORITIES)) { - Set authorityStrs = readSet(reader); - Set authorities = new HashSet(); - for (String s : authorityStrs) { - GrantedAuthority ga = new SimpleGrantedAuthority(s); - authorities.add(ga); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(CLIENT_ID)) { + client.setClientId(reader.nextString()); + } else if (name.equals(RESOURCE_IDS)) { + Set resourceIds = readSet(reader); + client.setResourceIds(resourceIds); + } else if (name.equals(SECRET)) { + client.setClientSecret(reader.nextString()); + } else if (name.equals(SCOPE)) { + Set scope = readSet(reader); + client.setScope(scope); + } else if (name.equals(AUTHORITIES)) { + Set authorityStrs = readSet(reader); + Set authorities = new HashSet(); + for (String s : authorityStrs) { + GrantedAuthority ga = new SimpleGrantedAuthority(s); + authorities.add(ga); + } + client.setAuthorities(authorities); + } else if (name.equals(ACCESS_TOKEN_VALIDITY_SECONDS)) { + client.setAccessTokenValiditySeconds(reader.nextInt()); + } else if (name.equals(REFRESH_TOKEN_VALIDITY_SECONDS)) { + client.setRefreshTokenValiditySeconds(reader.nextInt()); + } else if (name.equals(REDIRECT_URIS)) { + Set redirectUris = readSet(reader); + client.setRedirectUris(redirectUris); + } else if (name.equals(CLAIMS_REDIRECT_URIS)) { + Set claimsRedirectUris = readSet(reader); + client.setClaimsRedirectUris(claimsRedirectUris); + } else if (name.equals(NAME)) { + client.setClientName(reader.nextString()); + } else if (name.equals(URI)) { + client.setClientUri(reader.nextString()); + } else if (name.equals(LOGO_URI)) { + client.setLogoUri(reader.nextString()); + } else if (name.equals(CONTACTS)) { + Set contacts = readSet(reader); + client.setContacts(contacts); + } else if (name.equals(TOS_URI)) { + client.setTosUri(reader.nextString()); + } else if (name.equals(TOKEN_ENDPOINT_AUTH_METHOD)) { + AuthMethod am = AuthMethod.getByValue(reader.nextString()); + client.setTokenEndpointAuthMethod(am); + } else if (name.equals(GRANT_TYPES)) { + Set grantTypes = readSet(reader); + client.setGrantTypes(grantTypes); + } else if (name.equals(RESPONSE_TYPES)) { + Set responseTypes = readSet(reader); + client.setResponseTypes(responseTypes); + } else if (name.equals(POLICY_URI)) { + client.setPolicyUri(reader.nextString()); + } else if (name.equals(APPLICATION_TYPE)) { + AppType appType = AppType.getByValue(reader.nextString()); + client.setApplicationType(appType); + } else if (name.equals(SECTOR_IDENTIFIER_URI)) { + client.setSectorIdentifierUri(reader.nextString()); + } else if (name.equals(SUBJECT_TYPE)) { + SubjectType st = SubjectType.getByValue(reader.nextString()); + client.setSubjectType(st); + } else if (name.equals(JWKS_URI)) { + client.setJwksUri(reader.nextString()); + } else if (name.equals(JWKS)) { + try { + client.setJwks(JWKSet.parse(reader.nextString())); + } catch (ParseException e) { + logger.error("Couldn't parse JWK Set", e); + } + } else if (name.equals(REQUEST_OBJECT_SIGNING_ALG)) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setRequestObjectSigningAlg(alg); + } else if (name.equals(USER_INFO_ENCRYPTED_RESPONSE_ALG)) { + JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); + client.setUserInfoEncryptedResponseAlg(alg); + } else if (name.equals(USER_INFO_ENCRYPTED_RESPONSE_ENC)) { + EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); + client.setUserInfoEncryptedResponseEnc(alg); + } else if (name.equals(USER_INFO_SIGNED_RESPONSE_ALG)) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setUserInfoSignedResponseAlg(alg); + } else if (name.equals(ID_TOKEN_SIGNED_RESPONSE_ALG)) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setIdTokenSignedResponseAlg(alg); + } else if (name.equals(ID_TOKEN_ENCRYPTED_RESPONSE_ALG)) { + JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); + client.setIdTokenEncryptedResponseAlg(alg); + } else if (name.equals(ID_TOKEN_ENCRYPTED_RESPONSE_ENC)) { + EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); + client.setIdTokenEncryptedResponseEnc(alg); + } else if (name.equals(TOKEN_ENDPOINT_AUTH_SIGNING_ALG)) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setTokenEndpointAuthSigningAlg(alg); + } else if (name.equals(DEFAULT_MAX_AGE)) { + client.setDefaultMaxAge(reader.nextInt()); + } else if (name.equals(REQUIRE_AUTH_TIME)) { + client.setRequireAuthTime(reader.nextBoolean()); + } else if (name.equals(DEFAULT_ACR_VALUES)) { + Set defaultACRvalues = readSet(reader); + client.setDefaultACRvalues(defaultACRvalues); + } else if (name.equals("initiateLoginUri")) { + client.setInitiateLoginUri(reader.nextString()); + } else if (name.equals(POST_LOGOUT_REDIRECT_URI)) { + Set postLogoutUris = readSet(reader); + client.setPostLogoutRedirectUris(postLogoutUris); + } else if (name.equals(REQUEST_URIS)) { + Set requestUris = readSet(reader); + client.setRequestUris(requestUris); + } else if (name.equals(DESCRIPTION)) { + client.setClientDescription(reader.nextString()); + } else if (name.equals(ALLOW_INTROSPECTION)) { + client.setAllowIntrospection(reader.nextBoolean()); + } else if (name.equals(REUSE_REFRESH_TOKEN)) { + client.setReuseRefreshToken(reader.nextBoolean()); + } else if (name.equals(CLEAR_ACCESS_TOKENS_ON_REFRESH)) { + client.setClearAccessTokensOnRefresh(reader.nextBoolean()); + } else if (name.equals(DYNAMICALLY_REGISTERED)) { + client.setDynamicallyRegistered(reader.nextBoolean()); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - client.setAuthorities(authorities); - } else if (name.equals(ACCESS_TOKEN_VALIDITY_SECONDS)) { - client.setAccessTokenValiditySeconds(reader.nextInt()); - } else if (name.equals(REFRESH_TOKEN_VALIDITY_SECONDS)) { - client.setRefreshTokenValiditySeconds(reader.nextInt()); - } else if (name.equals(REDIRECT_URIS)) { - Set redirectUris = readSet(reader); - client.setRedirectUris(redirectUris); - } else if (name.equals(CLAIMS_REDIRECT_URIS)) { - Set claimsRedirectUris = readSet(reader); - client.setClaimsRedirectUris(claimsRedirectUris); - } else if (name.equals(NAME)) { - client.setClientName(reader.nextString()); - } else if (name.equals(URI)) { - client.setClientUri(reader.nextString()); - } else if (name.equals(LOGO_URI)) { - client.setLogoUri(reader.nextString()); - } else if (name.equals(CONTACTS)) { - Set contacts = readSet(reader); - client.setContacts(contacts); - } else if (name.equals(TOS_URI)) { - client.setTosUri(reader.nextString()); - } else if (name.equals(TOKEN_ENDPOINT_AUTH_METHOD)) { - AuthMethod am = AuthMethod.getByValue(reader.nextString()); - client.setTokenEndpointAuthMethod(am); - } else if (name.equals(GRANT_TYPES)) { - Set grantTypes = readSet(reader); - client.setGrantTypes(grantTypes); - } else if (name.equals(RESPONSE_TYPES)) { - Set responseTypes = readSet(reader); - client.setResponseTypes(responseTypes); - } else if (name.equals(POLICY_URI)) { - client.setPolicyUri(reader.nextString()); - } else if (name.equals(APPLICATION_TYPE)) { - AppType appType = AppType.getByValue(reader.nextString()); - client.setApplicationType(appType); - } else if (name.equals(SECTOR_IDENTIFIER_URI)) { - client.setSectorIdentifierUri(reader.nextString()); - } else if (name.equals(SUBJECT_TYPE)) { - SubjectType st = SubjectType.getByValue(reader.nextString()); - client.setSubjectType(st); - } else if (name.equals(JWKS_URI)) { - client.setJwksUri(reader.nextString()); - } else if (name.equals(JWKS)) { - try { - client.setJwks(JWKSet.parse(reader.nextString())); - } catch (ParseException e) { - logger.error("Couldn't parse JWK Set", e); - } - } else if (name.equals(REQUEST_OBJECT_SIGNING_ALG)) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setRequestObjectSigningAlg(alg); - } else if (name.equals(USER_INFO_ENCRYPTED_RESPONSE_ALG)) { - JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); - client.setUserInfoEncryptedResponseAlg(alg); - } else if (name.equals(USER_INFO_ENCRYPTED_RESPONSE_ENC)) { - EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); - client.setUserInfoEncryptedResponseEnc(alg); - } else if (name.equals(USER_INFO_SIGNED_RESPONSE_ALG)) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setUserInfoSignedResponseAlg(alg); - } else if (name.equals(ID_TOKEN_SIGNED_RESPONSE_ALG)) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setIdTokenSignedResponseAlg(alg); - } else if (name.equals(ID_TOKEN_ENCRYPTED_RESPONSE_ALG)) { - JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); - client.setIdTokenEncryptedResponseAlg(alg); - } else if (name.equals(ID_TOKEN_ENCRYPTED_RESPONSE_ENC)) { - EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); - client.setIdTokenEncryptedResponseEnc(alg); - } else if (name.equals(TOKEN_ENDPOINT_AUTH_SIGNING_ALG)) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setTokenEndpointAuthSigningAlg(alg); - } else if (name.equals(DEFAULT_MAX_AGE)) { - client.setDefaultMaxAge(reader.nextInt()); - } else if (name.equals(REQUIRE_AUTH_TIME)) { - client.setRequireAuthTime(reader.nextBoolean()); - } else if (name.equals(DEFAULT_ACR_VALUES)) { - Set defaultACRvalues = readSet(reader); - client.setDefaultACRvalues(defaultACRvalues); - } else if (name.equals("initiateLoginUri")) { - client.setInitiateLoginUri(reader.nextString()); - } else if (name.equals(POST_LOGOUT_REDIRECT_URI)) { - Set postLogoutUris = readSet(reader); - client.setPostLogoutRedirectUris(postLogoutUris); - } else if (name.equals(REQUEST_URIS)) { - Set requestUris = readSet(reader); - client.setRequestUris(requestUris); - } else if (name.equals(DESCRIPTION)) { - client.setClientDescription(reader.nextString()); - } else if (name.equals(ALLOW_INTROSPECTION)) { - client.setAllowIntrospection(reader.nextBoolean()); - } else if (name.equals(REUSE_REFRESH_TOKEN)) { - client.setReuseRefreshToken(reader.nextBoolean()); - } else if (name.equals(CLEAR_ACCESS_TOKENS_ON_REFRESH)) { - client.setClearAccessTokensOnRefresh(reader.nextBoolean()); - } else if (name.equals(DYNAMICALLY_REGISTERED)) { - client.setDynamicallyRegistered(reader.nextBoolean()); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -803,35 +798,35 @@ private void readSystemScopes(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(VALUE)) { - scope.setValue(reader.nextString()); - } else if (name.equals(DESCRIPTION)) { - scope.setDescription(reader.nextString()); - } else if (name.equals(RESTRICTED)) { - scope.setRestricted(reader.nextBoolean()); - } else if (name.equals(DEFAULT_SCOPE)) { - scope.setDefaultScope(reader.nextBoolean()); - } else if (name.equals(ICON)) { - scope.setIcon(reader.nextString()); - } else if (name.equals(STRUCTURED)) { - logger.warn("Found a structured scope, ignoring structure"); - } else if (name.equals(STRUCTURED_PARAMETER)) { - logger.warn("Found a structured scope, ignoring structure"); - } else { - logger.debug("found unexpected entry"); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(VALUE)) { + scope.setValue(reader.nextString()); + } else if (name.equals(DESCRIPTION)) { + scope.setDescription(reader.nextString()); + } else if (name.equals(RESTRICTED)) { + scope.setRestricted(reader.nextBoolean()); + } else if (name.equals(DEFAULT_SCOPE)) { + scope.setDefaultScope(reader.nextBoolean()); + } else if (name.equals(ICON)) { + scope.setIcon(reader.nextString()); + } else if (name.equals(STRUCTURED)) { + logger.warn("Found a structured scope, ignoring structure"); + } else if (name.equals(STRUCTURED_PARAMETER)) { + logger.warn("Found a structured scope, ignoring structure"); + } else { + logger.debug("found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -898,7 +893,7 @@ private void fixObjectReferences() { token.setApprovedSite(site); tokenRepository.saveAccessToken(token); } - + approvedSiteRepository.save(site); } logger.info("Done fixing object references."); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java index caa0110346..582d17f32c 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java @@ -172,11 +172,11 @@ public class MITREidDataService_1_3 extends MITREidDataServiceSupport implements private SystemScopeRepository sysScopeRepository; @Autowired(required = false) private List extensions = Collections.emptyList(); - + private static final String THIS_VERSION = MITREID_CONNECT_1_3; - - private MITREidDataServiceMaps maps = new MITREidDataServiceMaps(); - + + private MITREidDataServiceMaps maps = new MITREidDataServiceMaps(); + @Override public boolean supportsVersion(String version) { return THIS_VERSION.equals(version); @@ -233,7 +233,7 @@ public void exportData(JsonWriter writer) throws IOException { writer.beginArray(); writeSystemScopes(writer); writer.endArray(); - + for (MITREidDataServiceExtension extension : extensions) { if (extension.supportsVersion(THIS_VERSION)) { extension.exportExtensionData(writer); @@ -574,51 +574,51 @@ public void importData(JsonReader reader) throws IOException { while (reader.hasNext()) { JsonToken tok = reader.peek(); switch (tok) { - case NAME: - String name = reader.nextName(); - // find out which member it is - if (name.equals(CLIENTS)) { - readClients(reader); - } else if (name.equals(GRANTS)) { - readGrants(reader); - } else if (name.equals(WHITELISTEDSITES)) { - readWhitelistedSites(reader); - } else if (name.equals(BLACKLISTEDSITES)) { - readBlacklistedSites(reader); - } else if (name.equals(AUTHENTICATIONHOLDERS)) { - readAuthenticationHolders(reader); - } else if (name.equals(ACCESSTOKENS)) { - readAccessTokens(reader); - } else if (name.equals(REFRESHTOKENS)) { - readRefreshTokens(reader); - } else if (name.equals(SYSTEMSCOPES)) { - readSystemScopes(reader); - } else { - boolean processed = false; - for (MITREidDataServiceExtension extension : extensions) { - if (extension.supportsVersion(THIS_VERSION)) { - processed = extension.importExtensionData(name, reader); - if (processed) { - // if the extension processed data, break out of this inner loop - // (only the first extension to claim an extension point gets it) - break; + case NAME: + String name = reader.nextName(); + // find out which member it is + if (name.equals(CLIENTS)) { + readClients(reader); + } else if (name.equals(GRANTS)) { + readGrants(reader); + } else if (name.equals(WHITELISTEDSITES)) { + readWhitelistedSites(reader); + } else if (name.equals(BLACKLISTEDSITES)) { + readBlacklistedSites(reader); + } else if (name.equals(AUTHENTICATIONHOLDERS)) { + readAuthenticationHolders(reader); + } else if (name.equals(ACCESSTOKENS)) { + readAccessTokens(reader); + } else if (name.equals(REFRESHTOKENS)) { + readRefreshTokens(reader); + } else if (name.equals(SYSTEMSCOPES)) { + readSystemScopes(reader); + } else { + boolean processed = false; + for (MITREidDataServiceExtension extension : extensions) { + if (extension.supportsVersion(THIS_VERSION)) { + processed = extension.importExtensionData(name, reader); + if (processed) { + // if the extension processed data, break out of this inner loop + // (only the first extension to claim an extension point gets it) + break; + } } } + if (!processed) { + // unknown token, skip it + reader.skipValue(); + } } - if (!processed) { - // unknown token, skip it - reader.skipValue(); - } - } - break; - case END_OBJECT: - // the object ended, we're done here - reader.endObject(); - continue; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + break; + case END_OBJECT: + // the object ended, we're done here + reader.endObject(); + continue; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; } } fixObjectReferences(); @@ -630,7 +630,7 @@ public void importData(JsonReader reader) throws IOException { } maps.clearAll(); } - + /** * @param reader * @throws IOException @@ -649,37 +649,37 @@ private void readRefreshTokens(JsonReader reader) throws IOException { Long authHolderId = null; while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ID)) { - currentId = reader.nextLong(); - } else if (name.equals(EXPIRATION)) { - Date date = utcToDate(reader.nextString()); - token.setExpiration(date); - } else if (name.equals(VALUE)) { - String value = reader.nextString(); - try { - token.setJwt(JWTParser.parse(value)); - } catch (ParseException ex) { - logger.error("Unable to set refresh token value to {}", value, ex); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ID)) { + currentId = reader.nextLong(); + } else if (name.equals(EXPIRATION)) { + Date date = utcToDate(reader.nextString()); + token.setExpiration(date); + } else if (name.equals(VALUE)) { + String value = reader.nextString(); + try { + token.setJwt(JWTParser.parse(value)); + } catch (ParseException ex) { + logger.error("Unable to set refresh token value to {}", value, ex); + } + } else if (name.equals(CLIENT_ID)) { + clientId = reader.nextString(); + } else if (name.equals(AUTHENTICATION_HOLDER_ID)) { + authHolderId = reader.nextLong(); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - } else if (name.equals(CLIENT_ID)) { - clientId = reader.nextString(); - } else if (name.equals(AUTHENTICATION_HOLDER_ID)) { - authHolderId = reader.nextLong(); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -711,45 +711,45 @@ private void readAccessTokens(JsonReader reader) throws IOException { Long refreshTokenId = null; while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ID)) { - currentId = reader.nextLong(); - } else if (name.equals(EXPIRATION)) { - Date date = utcToDate(reader.nextString()); - token.setExpiration(date); - } else if (name.equals(VALUE)) { - String value = reader.nextString(); - try { - // all tokens are JWTs - token.setJwt(JWTParser.parse(value)); - } catch (ParseException ex) { - logger.error("Unable to set refresh token value to {}", value, ex); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ID)) { + currentId = reader.nextLong(); + } else if (name.equals(EXPIRATION)) { + Date date = utcToDate(reader.nextString()); + token.setExpiration(date); + } else if (name.equals(VALUE)) { + String value = reader.nextString(); + try { + // all tokens are JWTs + token.setJwt(JWTParser.parse(value)); + } catch (ParseException ex) { + logger.error("Unable to set refresh token value to {}", value, ex); + } + } else if (name.equals(CLIENT_ID)) { + clientId = reader.nextString(); + } else if (name.equals(AUTHENTICATION_HOLDER_ID)) { + authHolderId = reader.nextLong(); + } else if (name.equals(REFRESH_TOKEN_ID)) { + refreshTokenId = reader.nextLong(); + } else if (name.equals(SCOPE)) { + Set scope = readSet(reader); + token.setScope(scope); + } else if (name.equals(TYPE)) { + token.setTokenType(reader.nextString()); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - } else if (name.equals(CLIENT_ID)) { - clientId = reader.nextString(); - } else if (name.equals(AUTHENTICATION_HOLDER_ID)) { - authHolderId = reader.nextLong(); - } else if (name.equals(REFRESH_TOKEN_ID)) { - refreshTokenId = reader.nextLong(); - } else if (name.equals(SCOPE)) { - Set scope = readSet(reader); - token.setScope(scope); - } else if (name.equals(TYPE)) { - token.setTokenType(reader.nextString()); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -777,49 +777,49 @@ private void readAuthenticationHolders(JsonReader reader) throws IOException { Long currentId = null; while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ID)) { - currentId = reader.nextLong(); - } else if (name.equals(REQUEST_PARAMETERS)) { - ahe.setRequestParameters(readMap(reader)); - } else if (name.equals(CLIENT_ID)) { - ahe.setClientId(reader.nextString()); - } else if (name.equals(SCOPE)) { - ahe.setScope(readSet(reader)); - } else if (name.equals(RESOURCE_IDS)) { - ahe.setResourceIds(readSet(reader)); - } else if (name.equals(AUTHORITIES)) { - Set authorityStrs = readSet(reader); - Set authorities = new HashSet(); - for (String s : authorityStrs) { - GrantedAuthority ga = new SimpleGrantedAuthority(s); - authorities.add(ga); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ID)) { + currentId = reader.nextLong(); + } else if (name.equals(REQUEST_PARAMETERS)) { + ahe.setRequestParameters(readMap(reader)); + } else if (name.equals(CLIENT_ID)) { + ahe.setClientId(reader.nextString()); + } else if (name.equals(SCOPE)) { + ahe.setScope(readSet(reader)); + } else if (name.equals(RESOURCE_IDS)) { + ahe.setResourceIds(readSet(reader)); + } else if (name.equals(AUTHORITIES)) { + Set authorityStrs = readSet(reader); + Set authorities = new HashSet(); + for (String s : authorityStrs) { + GrantedAuthority ga = new SimpleGrantedAuthority(s); + authorities.add(ga); + } + ahe.setAuthorities(authorities); + } else if (name.equals(APPROVED)) { + ahe.setApproved(reader.nextBoolean()); + } else if (name.equals(REDIRECT_URI)) { + ahe.setRedirectUri(reader.nextString()); + } else if (name.equals(RESPONSE_TYPES)) { + ahe.setResponseTypes(readSet(reader)); + } else if (name.equals(EXTENSIONS)) { + ahe.setExtensions(readMap(reader)); + } else if (name.equals(SAVED_USER_AUTHENTICATION)) { + ahe.setUserAuth(readSavedUserAuthentication(reader)); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - ahe.setAuthorities(authorities); - } else if (name.equals(APPROVED)) { - ahe.setApproved(reader.nextBoolean()); - } else if (name.equals(REDIRECT_URI)) { - ahe.setRedirectUri(reader.nextString()); - } else if (name.equals(RESPONSE_TYPES)) { - ahe.setResponseTypes(readSet(reader)); - } else if (name.equals(EXTENSIONS)) { - ahe.setExtensions(readMap(reader)); - } else if (name.equals(SAVED_USER_AUTHENTICATION)) { - ahe.setUserAuth(readSavedUserAuthentication(reader)); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -842,35 +842,35 @@ private SavedUserAuthentication readSavedUserAuthentication(JsonReader reader) t while (reader.hasNext()) { switch(reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(NAME)) { - savedUserAuth.setName(reader.nextString()); - } else if (name.equals(SOURCE_CLASS)) { - savedUserAuth.setSourceClass(reader.nextString()); - } else if (name.equals(AUTHENTICATED)) { - savedUserAuth.setAuthenticated(reader.nextBoolean()); - } else if (name.equals(AUTHORITIES)) { - Set authorityStrs = readSet(reader); - Set authorities = new HashSet(); - for (String s : authorityStrs) { - GrantedAuthority ga = new SimpleGrantedAuthority(s); - authorities.add(ga); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(NAME)) { + savedUserAuth.setName(reader.nextString()); + } else if (name.equals(SOURCE_CLASS)) { + savedUserAuth.setSourceClass(reader.nextString()); + } else if (name.equals(AUTHENTICATED)) { + savedUserAuth.setAuthenticated(reader.nextBoolean()); + } else if (name.equals(AUTHORITIES)) { + Set authorityStrs = readSet(reader); + Set authorities = new HashSet(); + for (String s : authorityStrs) { + GrantedAuthority ga = new SimpleGrantedAuthority(s); + authorities.add(ga); + } + savedUserAuth.setAuthorities(authorities); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - savedUserAuth.setAuthorities(authorities); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } @@ -891,41 +891,41 @@ private void readGrants(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ID)) { - currentId = reader.nextLong(); - } else if (name.equals(ACCESS_DATE)) { - Date date = utcToDate(reader.nextString()); - site.setAccessDate(date); - } else if (name.equals(CLIENT_ID)) { - site.setClientId(reader.nextString()); - } else if (name.equals(CREATION_DATE)) { - Date date = utcToDate(reader.nextString()); - site.setCreationDate(date); - } else if (name.equals(TIMEOUT_DATE)) { - Date date = utcToDate(reader.nextString()); - site.setTimeoutDate(date); - } else if (name.equals(USER_ID)) { - site.setUserId(reader.nextString()); - } else if (name.equals(ALLOWED_SCOPES)) { - Set allowedScopes = readSet(reader); - site.setAllowedScopes(allowedScopes); - } else if (name.equals(APPROVED_ACCESS_TOKENS)) { - tokenIds = readSet(reader); - } else { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ID)) { + currentId = reader.nextLong(); + } else if (name.equals(ACCESS_DATE)) { + Date date = utcToDate(reader.nextString()); + site.setAccessDate(date); + } else if (name.equals(CLIENT_ID)) { + site.setClientId(reader.nextString()); + } else if (name.equals(CREATION_DATE)) { + Date date = utcToDate(reader.nextString()); + site.setCreationDate(date); + } else if (name.equals(TIMEOUT_DATE)) { + Date date = utcToDate(reader.nextString()); + site.setTimeoutDate(date); + } else if (name.equals(USER_ID)) { + site.setUserId(reader.nextString()); + } else if (name.equals(ALLOWED_SCOPES)) { + Set allowedScopes = readSet(reader); + site.setAllowedScopes(allowedScopes); + } else if (name.equals(APPROVED_ACCESS_TOKENS)) { + tokenIds = readSet(reader); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -939,7 +939,7 @@ private void readGrants(JsonReader reader) throws IOException { reader.endArray(); logger.info("Done reading grants"); } - + /** * @param reader * @throws IOException @@ -952,28 +952,28 @@ private void readWhitelistedSites(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (name.equals(ID)) { - currentId = reader.nextLong(); - } else if (name.equals(CLIENT_ID)) { - wlSite.setClientId(reader.nextString()); - } else if (name.equals(CREATOR_USER_ID)) { - wlSite.setCreatorUserId(reader.nextString()); - } else if (name.equals(ALLOWED_SCOPES)) { - Set allowedScopes = readSet(reader); - wlSite.setAllowedScopes(allowedScopes); - } else { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (name.equals(ID)) { + currentId = reader.nextLong(); + } else if (name.equals(CLIENT_ID)) { + wlSite.setClientId(reader.nextString()); + } else if (name.equals(CREATOR_USER_ID)) { + wlSite.setCreatorUserId(reader.nextString()); + } else if (name.equals(ALLOWED_SCOPES)) { + Set allowedScopes = readSet(reader); + wlSite.setAllowedScopes(allowedScopes); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -995,23 +995,23 @@ private void readBlacklistedSites(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (name.equals(ID)) { - reader.skipValue(); - } else if (name.equals(URI)) { - blSite.setUri(reader.nextString()); - } else { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (name.equals(ID)) { + reader.skipValue(); + } else if (name.equals(URI)) { + blSite.setUri(reader.nextString()); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -1032,152 +1032,152 @@ private void readClients(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(CLIENT_ID)) { - client.setClientId(reader.nextString()); - } else if (name.equals(RESOURCE_IDS)) { - Set resourceIds = readSet(reader); - client.setResourceIds(resourceIds); - } else if (name.equals(SECRET)) { - client.setClientSecret(reader.nextString()); - } else if (name.equals(SCOPE)) { - Set scope = readSet(reader); - client.setScope(scope); - } else if (name.equals(AUTHORITIES)) { - Set authorityStrs = readSet(reader); - Set authorities = new HashSet(); - for (String s : authorityStrs) { - GrantedAuthority ga = new SimpleGrantedAuthority(s); - authorities.add(ga); - } - client.setAuthorities(authorities); - } else if (name.equals(ACCESS_TOKEN_VALIDITY_SECONDS)) { - client.setAccessTokenValiditySeconds(reader.nextInt()); - } else if (name.equals(REFRESH_TOKEN_VALIDITY_SECONDS)) { - client.setRefreshTokenValiditySeconds(reader.nextInt()); - } else if (name.equals(ID_TOKEN_VALIDITY_SECONDS)) { - client.setIdTokenValiditySeconds(reader.nextInt()); - } else if (name.equals(DEVICE_CODE_VALIDITY_SECONDS)) { - client.setDeviceCodeValiditySeconds(reader.nextInt()); - } else if (name.equals(REDIRECT_URIS)) { - Set redirectUris = readSet(reader); - client.setRedirectUris(redirectUris); - } else if (name.equals(CLAIMS_REDIRECT_URIS)) { - Set claimsRedirectUris = readSet(reader); - client.setClaimsRedirectUris(claimsRedirectUris); - } else if (name.equals(NAME)) { - client.setClientName(reader.nextString()); - } else if (name.equals(URI)) { - client.setClientUri(reader.nextString()); - } else if (name.equals(LOGO_URI)) { - client.setLogoUri(reader.nextString()); - } else if (name.equals(CONTACTS)) { - Set contacts = readSet(reader); - client.setContacts(contacts); - } else if (name.equals(TOS_URI)) { - client.setTosUri(reader.nextString()); - } else if (name.equals(TOKEN_ENDPOINT_AUTH_METHOD)) { - AuthMethod am = AuthMethod.getByValue(reader.nextString()); - client.setTokenEndpointAuthMethod(am); - } else if (name.equals(GRANT_TYPES)) { - Set grantTypes = readSet(reader); - client.setGrantTypes(grantTypes); - } else if (name.equals(RESPONSE_TYPES)) { - Set responseTypes = readSet(reader); - client.setResponseTypes(responseTypes); - } else if (name.equals(POLICY_URI)) { - client.setPolicyUri(reader.nextString()); - } else if (name.equals(APPLICATION_TYPE)) { - AppType appType = AppType.getByValue(reader.nextString()); - client.setApplicationType(appType); - } else if (name.equals(SECTOR_IDENTIFIER_URI)) { - client.setSectorIdentifierUri(reader.nextString()); - } else if (name.equals(SUBJECT_TYPE)) { - SubjectType st = SubjectType.getByValue(reader.nextString()); - client.setSubjectType(st); - } else if (name.equals(JWKS_URI)) { - client.setJwksUri(reader.nextString()); - } else if (name.equals(JWKS)) { - try { - client.setJwks(JWKSet.parse(reader.nextString())); - } catch (ParseException e) { - logger.error("Couldn't parse JWK Set", e); - } - } else if (name.equals(REQUEST_OBJECT_SIGNING_ALG)) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setRequestObjectSigningAlg(alg); - } else if (name.equals(USER_INFO_ENCRYPTED_RESPONSE_ALG)) { - JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); - client.setUserInfoEncryptedResponseAlg(alg); - } else if (name.equals(USER_INFO_ENCRYPTED_RESPONSE_ENC)) { - EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); - client.setUserInfoEncryptedResponseEnc(alg); - } else if (name.equals(USER_INFO_SIGNED_RESPONSE_ALG)) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setUserInfoSignedResponseAlg(alg); - } else if (name.equals(ID_TOKEN_SIGNED_RESPONSE_ALG)) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setIdTokenSignedResponseAlg(alg); - } else if (name.equals(ID_TOKEN_ENCRYPTED_RESPONSE_ALG)) { - JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); - client.setIdTokenEncryptedResponseAlg(alg); - } else if (name.equals(ID_TOKEN_ENCRYPTED_RESPONSE_ENC)) { - EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); - client.setIdTokenEncryptedResponseEnc(alg); - } else if (name.equals(TOKEN_ENDPOINT_AUTH_SIGNING_ALG)) { - JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); - client.setTokenEndpointAuthSigningAlg(alg); - } else if (name.equals(DEFAULT_MAX_AGE)) { - client.setDefaultMaxAge(reader.nextInt()); - } else if (name.equals(REQUIRE_AUTH_TIME)) { - client.setRequireAuthTime(reader.nextBoolean()); - } else if (name.equals(DEFAULT_ACR_VALUES)) { - Set defaultACRvalues = readSet(reader); - client.setDefaultACRvalues(defaultACRvalues); - } else if (name.equals("initiateLoginUri")) { - client.setInitiateLoginUri(reader.nextString()); - } else if (name.equals(POST_LOGOUT_REDIRECT_URI)) { - Set postLogoutUris = readSet(reader); - client.setPostLogoutRedirectUris(postLogoutUris); - } else if (name.equals(REQUEST_URIS)) { - Set requestUris = readSet(reader); - client.setRequestUris(requestUris); - } else if (name.equals(DESCRIPTION)) { - client.setClientDescription(reader.nextString()); - } else if (name.equals(ALLOW_INTROSPECTION)) { - client.setAllowIntrospection(reader.nextBoolean()); - } else if (name.equals(REUSE_REFRESH_TOKEN)) { - client.setReuseRefreshToken(reader.nextBoolean()); - } else if (name.equals(CLEAR_ACCESS_TOKENS_ON_REFRESH)) { - client.setClearAccessTokensOnRefresh(reader.nextBoolean()); - } else if (name.equals(DYNAMICALLY_REGISTERED)) { - client.setDynamicallyRegistered(reader.nextBoolean()); - } else if (name.equals(CODE_CHALLENGE_METHOD)) { - client.setCodeChallengeMethod(PKCEAlgorithm.parse(reader.nextString())); - } else if (name.equals(SOFTWARE_ID)) { - client.setSoftwareId(reader.nextString()); - } else if (name.equals(SOFTWARE_VERSION)) { - client.setSoftwareVersion(reader.nextString()); - } else if (name.equals(SOFTWARE_STATEMENT)) { - try { - client.setSoftwareStatement(JWTParser.parse(reader.nextString())); - } catch (ParseException e) { - logger.error("Couldn't parse software statement", e); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(CLIENT_ID)) { + client.setClientId(reader.nextString()); + } else if (name.equals(RESOURCE_IDS)) { + Set resourceIds = readSet(reader); + client.setResourceIds(resourceIds); + } else if (name.equals(SECRET)) { + client.setClientSecret(reader.nextString()); + } else if (name.equals(SCOPE)) { + Set scope = readSet(reader); + client.setScope(scope); + } else if (name.equals(AUTHORITIES)) { + Set authorityStrs = readSet(reader); + Set authorities = new HashSet(); + for (String s : authorityStrs) { + GrantedAuthority ga = new SimpleGrantedAuthority(s); + authorities.add(ga); + } + client.setAuthorities(authorities); + } else if (name.equals(ACCESS_TOKEN_VALIDITY_SECONDS)) { + client.setAccessTokenValiditySeconds(reader.nextInt()); + } else if (name.equals(REFRESH_TOKEN_VALIDITY_SECONDS)) { + client.setRefreshTokenValiditySeconds(reader.nextInt()); + } else if (name.equals(ID_TOKEN_VALIDITY_SECONDS)) { + client.setIdTokenValiditySeconds(reader.nextInt()); + } else if (name.equals(DEVICE_CODE_VALIDITY_SECONDS)) { + client.setDeviceCodeValiditySeconds(reader.nextInt()); + } else if (name.equals(REDIRECT_URIS)) { + Set redirectUris = readSet(reader); + client.setRedirectUris(redirectUris); + } else if (name.equals(CLAIMS_REDIRECT_URIS)) { + Set claimsRedirectUris = readSet(reader); + client.setClaimsRedirectUris(claimsRedirectUris); + } else if (name.equals(NAME)) { + client.setClientName(reader.nextString()); + } else if (name.equals(URI)) { + client.setClientUri(reader.nextString()); + } else if (name.equals(LOGO_URI)) { + client.setLogoUri(reader.nextString()); + } else if (name.equals(CONTACTS)) { + Set contacts = readSet(reader); + client.setContacts(contacts); + } else if (name.equals(TOS_URI)) { + client.setTosUri(reader.nextString()); + } else if (name.equals(TOKEN_ENDPOINT_AUTH_METHOD)) { + AuthMethod am = AuthMethod.getByValue(reader.nextString()); + client.setTokenEndpointAuthMethod(am); + } else if (name.equals(GRANT_TYPES)) { + Set grantTypes = readSet(reader); + client.setGrantTypes(grantTypes); + } else if (name.equals(RESPONSE_TYPES)) { + Set responseTypes = readSet(reader); + client.setResponseTypes(responseTypes); + } else if (name.equals(POLICY_URI)) { + client.setPolicyUri(reader.nextString()); + } else if (name.equals(APPLICATION_TYPE)) { + AppType appType = AppType.getByValue(reader.nextString()); + client.setApplicationType(appType); + } else if (name.equals(SECTOR_IDENTIFIER_URI)) { + client.setSectorIdentifierUri(reader.nextString()); + } else if (name.equals(SUBJECT_TYPE)) { + SubjectType st = SubjectType.getByValue(reader.nextString()); + client.setSubjectType(st); + } else if (name.equals(JWKS_URI)) { + client.setJwksUri(reader.nextString()); + } else if (name.equals(JWKS)) { + try { + client.setJwks(JWKSet.parse(reader.nextString())); + } catch (ParseException e) { + logger.error("Couldn't parse JWK Set", e); + } + } else if (name.equals(REQUEST_OBJECT_SIGNING_ALG)) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setRequestObjectSigningAlg(alg); + } else if (name.equals(USER_INFO_ENCRYPTED_RESPONSE_ALG)) { + JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); + client.setUserInfoEncryptedResponseAlg(alg); + } else if (name.equals(USER_INFO_ENCRYPTED_RESPONSE_ENC)) { + EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); + client.setUserInfoEncryptedResponseEnc(alg); + } else if (name.equals(USER_INFO_SIGNED_RESPONSE_ALG)) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setUserInfoSignedResponseAlg(alg); + } else if (name.equals(ID_TOKEN_SIGNED_RESPONSE_ALG)) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setIdTokenSignedResponseAlg(alg); + } else if (name.equals(ID_TOKEN_ENCRYPTED_RESPONSE_ALG)) { + JWEAlgorithm alg = JWEAlgorithm.parse(reader.nextString()); + client.setIdTokenEncryptedResponseAlg(alg); + } else if (name.equals(ID_TOKEN_ENCRYPTED_RESPONSE_ENC)) { + EncryptionMethod alg = EncryptionMethod.parse(reader.nextString()); + client.setIdTokenEncryptedResponseEnc(alg); + } else if (name.equals(TOKEN_ENDPOINT_AUTH_SIGNING_ALG)) { + JWSAlgorithm alg = JWSAlgorithm.parse(reader.nextString()); + client.setTokenEndpointAuthSigningAlg(alg); + } else if (name.equals(DEFAULT_MAX_AGE)) { + client.setDefaultMaxAge(reader.nextInt()); + } else if (name.equals(REQUIRE_AUTH_TIME)) { + client.setRequireAuthTime(reader.nextBoolean()); + } else if (name.equals(DEFAULT_ACR_VALUES)) { + Set defaultACRvalues = readSet(reader); + client.setDefaultACRvalues(defaultACRvalues); + } else if (name.equals("initiateLoginUri")) { + client.setInitiateLoginUri(reader.nextString()); + } else if (name.equals(POST_LOGOUT_REDIRECT_URI)) { + Set postLogoutUris = readSet(reader); + client.setPostLogoutRedirectUris(postLogoutUris); + } else if (name.equals(REQUEST_URIS)) { + Set requestUris = readSet(reader); + client.setRequestUris(requestUris); + } else if (name.equals(DESCRIPTION)) { + client.setClientDescription(reader.nextString()); + } else if (name.equals(ALLOW_INTROSPECTION)) { + client.setAllowIntrospection(reader.nextBoolean()); + } else if (name.equals(REUSE_REFRESH_TOKEN)) { + client.setReuseRefreshToken(reader.nextBoolean()); + } else if (name.equals(CLEAR_ACCESS_TOKENS_ON_REFRESH)) { + client.setClearAccessTokensOnRefresh(reader.nextBoolean()); + } else if (name.equals(DYNAMICALLY_REGISTERED)) { + client.setDynamicallyRegistered(reader.nextBoolean()); + } else if (name.equals(CODE_CHALLENGE_METHOD)) { + client.setCodeChallengeMethod(PKCEAlgorithm.parse(reader.nextString())); + } else if (name.equals(SOFTWARE_ID)) { + client.setSoftwareId(reader.nextString()); + } else if (name.equals(SOFTWARE_VERSION)) { + client.setSoftwareVersion(reader.nextString()); + } else if (name.equals(SOFTWARE_STATEMENT)) { + try { + client.setSoftwareStatement(JWTParser.parse(reader.nextString())); + } catch (ParseException e) { + logger.error("Couldn't parse software statement", e); + } + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -1201,31 +1201,31 @@ private void readSystemScopes(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(VALUE)) { - scope.setValue(reader.nextString()); - } else if (name.equals(DESCRIPTION)) { - scope.setDescription(reader.nextString()); - } else if (name.equals(RESTRICTED)) { - scope.setRestricted(reader.nextBoolean()); - } else if (name.equals(DEFAULT_SCOPE)) { - scope.setDefaultScope(reader.nextBoolean()); - } else if (name.equals(ICON)) { - scope.setIcon(reader.nextString()); - } else { - logger.debug("found unexpected entry"); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(VALUE)) { + scope.setValue(reader.nextString()); + } else if (name.equals(DESCRIPTION)) { + scope.setDescription(reader.nextString()); + } else if (name.equals(RESTRICTED)) { + scope.setRestricted(reader.nextBoolean()); + } else if (name.equals(DEFAULT_SCOPE)) { + scope.setDefaultScope(reader.nextBoolean()); + } else if (name.equals(ICON)) { + scope.setIcon(reader.nextString()); + } else { + logger.debug("found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -1292,7 +1292,7 @@ private void fixObjectReferences() { token.setApprovedSite(site); tokenRepository.saveAccessToken(token); } - + approvedSiteRepository.save(site); } /* @@ -1304,7 +1304,7 @@ private void fixObjectReferences() { refreshTokenOldToNewIdMap.clear(); accessTokenOldToNewIdMap.clear(); grantOldToNewIdMap.clear(); - */ + */ logger.info("Done fixing object references."); } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MatchLoginHintsAgainstUsers.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MatchLoginHintsAgainstUsers.java index a6db39604b..d8c30f06a4 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MatchLoginHintsAgainstUsers.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MatchLoginHintsAgainstUsers.java @@ -33,7 +33,7 @@ public class MatchLoginHintsAgainstUsers implements LoginHintExtracter { @Autowired private UserInfoService userInfoService; - + /* (non-Javadoc) * @see org.mitre.openid.connect.service.LoginHintTester#useHint(java.lang.String) */ diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/PassAllLoginHints.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/PassAllLoginHints.java index c3b9d6bcda..97b2181751 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/PassAllLoginHints.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/PassAllLoginHints.java @@ -21,7 +21,7 @@ /** * Sends all login hints through to the login page regardless of setup. - * + * * @author jricher * */ diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/RemoveLoginHintsWithHTTP.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/RemoveLoginHintsWithHTTP.java index 2465ce7992..4c036ba2a4 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/RemoveLoginHintsWithHTTP.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/RemoveLoginHintsWithHTTP.java @@ -23,7 +23,7 @@ /** * Passes login hints that don't start with "http" - * + * * @author jricher * */ diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/UUIDPairwiseIdentiferService.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/UUIDPairwiseIdentiferService.java index 79baf4c597..088fedb887 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/UUIDPairwiseIdentiferService.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/UUIDPairwiseIdentiferService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.service.impl; diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectTokenEnhancer.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectTokenEnhancer.java index 6c7a88acfd..44f9fd947d 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectTokenEnhancer.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/ConnectTokenEnhancer.java @@ -20,15 +20,12 @@ import java.util.UUID; import org.mitre.jwt.signer.service.JWTSigningAndValidationService; -import org.mitre.jwt.signer.service.impl.JWKSetCacheService; -import org.mitre.jwt.signer.service.impl.SymmetricKeyJWTValidatorCacheService; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.oauth2.service.ClientDetailsEntityService; import org.mitre.oauth2.service.SystemScopeService; import org.mitre.openid.connect.config.ConfigurationPropertiesBean; import org.mitre.openid.connect.model.UserInfo; -import org.mitre.openid.connect.service.ApprovedSiteService; import org.mitre.openid.connect.service.OIDCTokenService; import org.mitre.openid.connect.service.UserInfoService; import org.slf4j.Logger; @@ -66,22 +63,12 @@ public class ConnectTokenEnhancer implements TokenEnhancer { @Autowired private ClientDetailsEntityService clientService; - @Autowired - private ApprovedSiteService approvedSiteService; - @Autowired private UserInfoService userInfoService; @Autowired private OIDCTokenService connectTokenService; - @Autowired - private JWKSetCacheService encryptors; - - @Autowired - private SymmetricKeyJWTValidatorCacheService symmetricCacheService; - - @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { @@ -98,12 +85,12 @@ public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentica .expirationTime(token.getExpiration()) .subject(authentication.getName()) .jwtID(UUID.randomUUID().toString()); // set a random NONCE in the middle of it - + String audience = (String) authentication.getOAuth2Request().getExtensions().get("aud"); if (!Strings.isNullOrEmpty(audience)) { builder.audience(Lists.newArrayList(audience)); } - + JWTClaimsSet claims = builder.build(); JWSAlgorithm signingAlg = jwtService.getDefaultSigningAlgorithm(); @@ -121,7 +108,7 @@ public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentica * may or may not include the scope parameter. As long as the AuthorizationRequest * has the proper scope, we can consider this a valid OpenID Connect request. Otherwise, * we consider it to be a vanilla OAuth2 request. - * + * * Also, there must be a user authentication involved in the request for it to be considered * OIDC and not OAuth, so we check for that as well. */ @@ -171,6 +158,6 @@ public ClientDetailsEntityService getClientService() { public void setClientService(ClientDetailsEntityService clientService) { this.clientService = clientService; } - + } \ No newline at end of file diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/TofuUserApprovalHandler.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/TofuUserApprovalHandler.java index 5e2aadfbc1..316204a832 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/token/TofuUserApprovalHandler.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/token/TofuUserApprovalHandler.java @@ -31,7 +31,6 @@ import javax.servlet.http.HttpSession; -import org.mitre.oauth2.model.SystemScope; import org.mitre.oauth2.service.SystemScopeService; import org.mitre.openid.connect.model.ApprovedSite; import org.mitre.openid.connect.model.WhitelistedSite; @@ -55,13 +54,13 @@ /** * Custom User Approval Handler implementation which uses a concept of a whitelist, * blacklist, and greylist. - * + * * Blacklisted sites will be caught and handled before this * point. - * + * * Whitelisted sites will be automatically approved, and an ApprovedSite entry will * be created for the site the first time a given user access it. - * + * * All other sites fall into the greylist - the user will be presented with the user * approval page upon their first visit * @author aanganes @@ -85,12 +84,12 @@ public class TofuUserApprovalHandler implements UserApprovalHandler { /** * Check if the user has already stored a positive approval decision for this site; or if the * site is whitelisted, approve it automatically. - * + * * Otherwise, return false so that the user will see the approval page and can make their own decision. - * + * * @param authorizationRequest the incoming authorization request * @param userAuthentication the Principal representing the currently-logged-in user - * + * * @return true if the site is approved, false otherwise */ @Override @@ -111,12 +110,12 @@ public boolean isApproved(AuthorizationRequest authorizationRequest, Authenticat /** * Check if the user has already stored a positive approval decision for this site; or if the * site is whitelisted, approve it automatically. - * + * * Otherwise the user will be directed to the approval page and can make their own decision. - * + * * @param authorizationRequest the incoming authorization request * @param userAuthentication the Principal representing the currently-logged-in user - * + * * @return the updated AuthorizationRequest */ @Override @@ -241,7 +240,7 @@ public AuthorizationRequest updateAfterApproval(AuthorizationRequest authorizati /** * Get the auth time out of the current session and add it to the * auth request in the extensions map. - * + * * @param authorizationRequest */ private void setAuthTime(AuthorizationRequest authorizationRequest) { diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/util/IdTokenHashUtils.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/util/IdTokenHashUtils.java index 43b7e0ed83..0f40b69d65 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/util/IdTokenHashUtils.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/util/IdTokenHashUtils.java @@ -31,9 +31,9 @@ /** * Utility class for generating hashes for access tokens and authorization codes * to be included in an ID Token. - * + * * @author Amanda Anganes - * + * */ public class IdTokenHashUtils { @@ -44,7 +44,7 @@ public class IdTokenHashUtils { /** * Compute the SHA hash of an authorization code - * + * * @param signingAlg * @param code * @return @@ -55,7 +55,7 @@ public static Base64URL getCodeHash(JWSAlgorithm signingAlg, String code) { /** * Compute the SHA hash of a token - * + * * @param signingAlg * @param token * @return diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/AbstractClientEntityView.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/AbstractClientEntityView.java index 5b3229c429..465e424654 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/AbstractClientEntityView.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/AbstractClientEntityView.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.view; @@ -37,15 +37,11 @@ import com.google.gson.ExclusionStrategy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; -import com.google.gson.JsonParseException; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; -import com.nimbusds.jose.Algorithm; import com.nimbusds.jose.EncryptionMethod; import com.nimbusds.jose.JWEAlgorithm; import com.nimbusds.jose.JWSAlgorithm; @@ -53,12 +49,12 @@ import com.nimbusds.jwt.JWT; /** - * + * * Abstract superclass for client entity view, used with the ClientApi. - * + * * @see ClientEntityViewForUsers * @see ClientEntityViewForAdmins - * + * * @author jricher * */ @@ -71,70 +67,71 @@ public abstract class AbstractClientEntityView extends AbstractView { private JsonParser parser = new JsonParser(); private Gson gson = new GsonBuilder() - .setExclusionStrategies(getExclusionStrategy()) - .registerTypeAdapter(JWSAlgorithm.class, new JsonSerializer() { - @Override - public JsonElement serialize(JWSAlgorithm src, Type typeOfSrc, JsonSerializationContext context) { - if (src != null) { - return new JsonPrimitive(src.getName()); - } else { - return null; - } - } - }) - .registerTypeAdapter(JWEAlgorithm.class, new JsonSerializer() { - @Override - public JsonElement serialize(JWEAlgorithm src, Type typeOfSrc, JsonSerializationContext context) { - if (src != null) { - return new JsonPrimitive(src.getName()); - } else { - return null; - } - } - }) - .registerTypeAdapter(EncryptionMethod.class, new JsonSerializer() { - @Override - public JsonElement serialize(EncryptionMethod src, Type typeOfSrc, JsonSerializationContext context) { - if (src != null) { - return new JsonPrimitive(src.getName()); - } else { - return null; - } - } - }) - .registerTypeAdapter(JWKSet.class, new JsonSerializer() { - @Override - public JsonElement serialize(JWKSet src, Type typeOfSrc, JsonSerializationContext context) { - if (src != null) { - return parser.parse(src.toString()); - } else { - return null; - } - } - }) - .registerTypeAdapter(JWT.class, new JsonSerializer() { - @Override - public JsonElement serialize(JWT src, Type typeOfSrc, JsonSerializationContext context) { - if (src != null) { - return new JsonPrimitive(src.serialize()); - } else { - return null; - } - } - - }) - .registerTypeAdapter(PKCEAlgorithm.class, new JsonSerializer() { - public JsonPrimitive serialize(PKCEAlgorithm src, Type typeOfSrc, JsonSerializationContext context) { - if (src != null) { - return new JsonPrimitive(src.getName()); - } else { - return null; - } - } - }) - .serializeNulls() - .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") - .create(); + .setExclusionStrategies(getExclusionStrategy()) + .registerTypeAdapter(JWSAlgorithm.class, new JsonSerializer() { + @Override + public JsonElement serialize(JWSAlgorithm src, Type typeOfSrc, JsonSerializationContext context) { + if (src != null) { + return new JsonPrimitive(src.getName()); + } else { + return null; + } + } + }) + .registerTypeAdapter(JWEAlgorithm.class, new JsonSerializer() { + @Override + public JsonElement serialize(JWEAlgorithm src, Type typeOfSrc, JsonSerializationContext context) { + if (src != null) { + return new JsonPrimitive(src.getName()); + } else { + return null; + } + } + }) + .registerTypeAdapter(EncryptionMethod.class, new JsonSerializer() { + @Override + public JsonElement serialize(EncryptionMethod src, Type typeOfSrc, JsonSerializationContext context) { + if (src != null) { + return new JsonPrimitive(src.getName()); + } else { + return null; + } + } + }) + .registerTypeAdapter(JWKSet.class, new JsonSerializer() { + @Override + public JsonElement serialize(JWKSet src, Type typeOfSrc, JsonSerializationContext context) { + if (src != null) { + return parser.parse(src.toString()); + } else { + return null; + } + } + }) + .registerTypeAdapter(JWT.class, new JsonSerializer() { + @Override + public JsonElement serialize(JWT src, Type typeOfSrc, JsonSerializationContext context) { + if (src != null) { + return new JsonPrimitive(src.serialize()); + } else { + return null; + } + } + + }) + .registerTypeAdapter(PKCEAlgorithm.class, new JsonSerializer() { + @Override + public JsonPrimitive serialize(PKCEAlgorithm src, Type typeOfSrc, JsonSerializationContext context) { + if (src != null) { + return new JsonPrimitive(src.getName()); + } else { + return null; + } + } + }) + .serializeNulls() + .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") + .create(); /** diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/ClientEntityViewForAdmins.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/ClientEntityViewForAdmins.java index 88ee0c60af..98f9d93833 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/ClientEntityViewForAdmins.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/ClientEntityViewForAdmins.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.view; @@ -29,9 +29,9 @@ import com.google.gson.FieldAttributes; /** - * + * * View bean for full view of client entity, for admins. - * + * * @see ClientEntityViewForUsers * @author jricher * diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/ClientEntityViewForUsers.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/ClientEntityViewForUsers.java index 4aaf55e8df..251fda1765 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/ClientEntityViewForUsers.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/ClientEntityViewForUsers.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.view; @@ -29,9 +29,9 @@ import com.google.gson.FieldAttributes; /** - * + * * View bean for field-limited view of client entity, for regular users. - * + * * @see AbstractClientEntityView * @see ClientEntityViewForAdmins * @author jricher diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/ClientInformationResponseView.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/ClientInformationResponseView.java index 1ab7c8452d..bce6a5532e 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/ClientInformationResponseView.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/ClientInformationResponseView.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.view; @@ -40,10 +40,10 @@ import com.google.gson.JsonObject; /** - * + * * Provides representation of a client's registration metadata, to be shown from the dynamic registration endpoint * on the client_register and rotate_secret operations. - * + * * @author jricher * */ diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/HttpCodeView.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/HttpCodeView.java index aa45f78ec8..98f69c6622 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/HttpCodeView.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/HttpCodeView.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.view; diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/JsonApprovedSiteView.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/JsonApprovedSiteView.java index 09daa7944c..5db6ecff6c 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/JsonApprovedSiteView.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/JsonApprovedSiteView.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.view; @@ -61,40 +61,40 @@ public class JsonApprovedSiteView extends AbstractView { public static final String VIEWNAME = "jsonApprovedSiteView"; private Gson gson = new GsonBuilder() - .setExclusionStrategies(new ExclusionStrategy() { - - @Override - public boolean shouldSkipField(FieldAttributes f) { - - return false; - } - - @Override - public boolean shouldSkipClass(Class clazz) { - // skip the JPA binding wrapper - if (clazz.equals(BeanPropertyBindingResult.class)) { - return true; - } - return false; - } - - }) - .registerTypeAdapter(OAuth2AccessTokenEntity.class, new JsonSerializer() { - @Override - public JsonElement serialize(OAuth2AccessTokenEntity src, - Type typeOfSrc, JsonSerializationContext context) { - return new JsonPrimitive(src.getId()); - } - }) - .registerTypeAdapter(WhitelistedSite.class, new JsonSerializer() { - @Override - public JsonElement serialize(WhitelistedSite src, Type typeOfSrc, JsonSerializationContext context) { - return new JsonPrimitive(src.getId()); - } - }) - .serializeNulls() - .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") - .create(); + .setExclusionStrategies(new ExclusionStrategy() { + + @Override + public boolean shouldSkipField(FieldAttributes f) { + + return false; + } + + @Override + public boolean shouldSkipClass(Class clazz) { + // skip the JPA binding wrapper + if (clazz.equals(BeanPropertyBindingResult.class)) { + return true; + } + return false; + } + + }) + .registerTypeAdapter(OAuth2AccessTokenEntity.class, new JsonSerializer() { + @Override + public JsonElement serialize(OAuth2AccessTokenEntity src, + Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.getId()); + } + }) + .registerTypeAdapter(WhitelistedSite.class, new JsonSerializer() { + @Override + public JsonElement serialize(WhitelistedSite src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.getId()); + } + }) + .serializeNulls() + .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") + .create(); @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) { diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/JsonEntityView.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/JsonEntityView.java index a5bbd6e7bf..b78a5f3c4c 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/JsonEntityView.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/JsonEntityView.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.view; @@ -56,27 +56,27 @@ public class JsonEntityView extends AbstractView { public static final String VIEWNAME = "jsonEntityView"; private Gson gson = new GsonBuilder() - .setExclusionStrategies(new ExclusionStrategy() { - - @Override - public boolean shouldSkipField(FieldAttributes f) { - - return false; - } - - @Override - public boolean shouldSkipClass(Class clazz) { - // skip the JPA binding wrapper - if (clazz.equals(BeanPropertyBindingResult.class)) { - return true; - } - return false; - } - - }) - .serializeNulls() - .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") - .create(); + .setExclusionStrategies(new ExclusionStrategy() { + + @Override + public boolean shouldSkipField(FieldAttributes f) { + + return false; + } + + @Override + public boolean shouldSkipClass(Class clazz) { + // skip the JPA binding wrapper + if (clazz.equals(BeanPropertyBindingResult.class)) { + return true; + } + return false; + } + + }) + .serializeNulls() + .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") + .create(); @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) { diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/JsonErrorView.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/JsonErrorView.java index 2fa26dbacd..8ad0380cae 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/JsonErrorView.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/JsonErrorView.java @@ -46,12 +46,12 @@ public class JsonErrorView extends AbstractView { /** - * + * */ public static final String ERROR_MESSAGE = "errorMessage"; /** - * + * */ public static final String ERROR = "error"; @@ -63,27 +63,27 @@ public class JsonErrorView extends AbstractView { public static final String VIEWNAME = "jsonErrorView"; private Gson gson = new GsonBuilder() - .setExclusionStrategies(new ExclusionStrategy() { - - @Override - public boolean shouldSkipField(FieldAttributes f) { - - return false; - } - - @Override - public boolean shouldSkipClass(Class clazz) { - // skip the JPA binding wrapper - if (clazz.equals(BeanPropertyBindingResult.class)) { - return true; - } - return false; - } - - }) - .serializeNulls() - .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") - .create(); + .setExclusionStrategies(new ExclusionStrategy() { + + @Override + public boolean shouldSkipField(FieldAttributes f) { + + return false; + } + + @Override + public boolean shouldSkipClass(Class clazz) { + // skip the JPA binding wrapper + if (clazz.equals(BeanPropertyBindingResult.class)) { + return true; + } + return false; + } + + }) + .serializeNulls() + .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") + .create(); @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) { diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoJWTView.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoJWTView.java index 405348de12..8996133927 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoJWTView.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoJWTView.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.view; @@ -99,11 +99,11 @@ protected void writeOut(JsonObject json, Map model, response.setContentType(JOSE_MEDIA_TYPE_VALUE); JWTClaimsSet claims = new JWTClaimsSet.Builder(JWTClaimsSet.parse(writer.toString())) - .audience(Lists.newArrayList(client.getClientId())) - .issuer(config.getIssuer()) - .issueTime(new Date()) - .jwtID(UUID.randomUUID().toString()) // set a random NONCE in the middle of it - .build(); + .audience(Lists.newArrayList(client.getClientId())) + .issuer(config.getIssuer()) + .issueTime(new Date()) + .jwtID(UUID.randomUUID().toString()) // set a random NONCE in the middle of it + .build(); if (client.getUserInfoEncryptedResponseAlg() != null && !client.getUserInfoEncryptedResponseAlg().equals(Algorithm.NONE) diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java index 438a247e66..c1a4fa4df1 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoView.java @@ -85,7 +85,7 @@ public boolean shouldSkipClass(Class clazz) { /* * (non-Javadoc) - * + * * @see * org.springframework.web.servlet.view.AbstractView#renderMergedOutputModel * (java.util.Map, javax.servlet.http.HttpServletRequest, @@ -129,10 +129,10 @@ protected void writeOut(JsonObject json, Map model, HttpServletR /** * Build a JSON response according to the request object received. - * + * * Claims requested in requestObj.userinfo.claims are added to any * claims corresponding to requested scopes, if any. - * + * * @param ui the UserInfo to filter * @param scope the allowed scopes to filter by * @param authorizedClaims the claims authorized by the client or user @@ -169,7 +169,7 @@ private JsonObject toJsonFromRequestObj(UserInfo ui, Set scope, JsonObje } /** - * Pull the claims that have been targeted into a set for processing. + * Pull the claims that have been targeted into a set for processing. * Returns an empty set if the input is null. * @param claims the claims request to process */ diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ApprovedSiteAPI.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ApprovedSiteAPI.java index 5843d37bf3..2bcb53cc61 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ApprovedSiteAPI.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ApprovedSiteAPI.java @@ -15,14 +15,13 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.web; import java.security.Principal; import java.util.Collection; -import org.mitre.oauth2.service.OAuth2TokenEntityService; import org.mitre.openid.connect.model.ApprovedSite; import org.mitre.openid.connect.service.ApprovedSiteService; import org.mitre.openid.connect.view.HttpCodeView; @@ -55,9 +54,6 @@ public class ApprovedSiteAPI { @Autowired private ApprovedSiteService approvedSiteService; - @Autowired - private OAuth2TokenEntityService tokenServices; - /** * Logger for this class */ @@ -80,7 +76,7 @@ public String getAllApprovedSites(ModelMap m, Principal p) { /** * Delete an approved site - * + * */ @RequestMapping(value="/{id}", method = RequestMethod.DELETE) public String deleteApprovedSite(@PathVariable("id") Long id, ModelMap m, Principal p) { diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/AuthenticationTimeStamper.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/AuthenticationTimeStamper.java index 26824ed6d1..1acbc687f6 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/AuthenticationTimeStamper.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/AuthenticationTimeStamper.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.web; @@ -37,7 +37,7 @@ /** * This class sets a timestamp on the current HttpSession * when someone successfully authenticates. - * + * * @author jricher * */ diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/BlacklistAPI.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/BlacklistAPI.java index d8635f7e8a..7a45907032 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/BlacklistAPI.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/BlacklistAPI.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.web; @@ -168,7 +168,7 @@ public String updateBlacklistedSite(@PathVariable("id") Long id, @RequestBody St /** * Delete a blacklisted site - * + * */ @RequestMapping(value="/{id}", method = RequestMethod.DELETE) public String deleteBlacklistedSite(@PathVariable("id") Long id, ModelMap m) { diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ClientAPI.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ClientAPI.java index 93accfe93e..7d924f14d8 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ClientAPI.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ClientAPI.java @@ -134,7 +134,7 @@ public class ClientAPI { @Autowired private ClientLogoLoadingService clientLogoLoadingService; - + @Autowired @Qualifier("clientAssertionValidator") private AssertionValidator assertionValidator; @@ -142,76 +142,77 @@ public class ClientAPI { private JsonParser parser = new JsonParser(); private Gson gson = new GsonBuilder() - .serializeNulls() - .registerTypeAdapter(JWSAlgorithm.class, new JsonDeserializer() { - @Override - public JWSAlgorithm deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - if (json.isJsonPrimitive()) { - return JWSAlgorithm.parse(json.getAsString()); - } else { - return null; - } - } - }) - .registerTypeAdapter(JWEAlgorithm.class, new JsonDeserializer() { - @Override - public JWEAlgorithm deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - if (json.isJsonPrimitive()) { - return JWEAlgorithm.parse(json.getAsString()); - } else { - return null; - } - } - }) - .registerTypeAdapter(EncryptionMethod.class, new JsonDeserializer() { - @Override - public EncryptionMethod deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - if (json.isJsonPrimitive()) { - return EncryptionMethod.parse(json.getAsString()); - } else { - return null; - } - } - }) - .registerTypeAdapter(JWKSet.class, new JsonDeserializer() { - @Override - public JWKSet deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - if (json.isJsonObject()) { - try { - return JWKSet.parse(json.toString()); - } catch (ParseException e) { - return null; + .serializeNulls() + .registerTypeAdapter(JWSAlgorithm.class, new JsonDeserializer() { + @Override + public JWSAlgorithm deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (json.isJsonPrimitive()) { + return JWSAlgorithm.parse(json.getAsString()); + } else { + return null; + } } - } else { - return null; - } - } - }) - .registerTypeAdapter(JWT.class, new JsonDeserializer() { - @Override - public JWT deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - if (json.isJsonPrimitive()) { - try { - return JWTParser.parse(json.getAsString()); - } catch (ParseException e) { - return null; + }) + .registerTypeAdapter(JWEAlgorithm.class, new JsonDeserializer() { + @Override + public JWEAlgorithm deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (json.isJsonPrimitive()) { + return JWEAlgorithm.parse(json.getAsString()); + } else { + return null; + } } - } else { - return null; - } - } - }) - .registerTypeAdapter(PKCEAlgorithm.class, new JsonDeserializer() { - public PKCEAlgorithm deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - if (json.isJsonPrimitive()) { - return PKCEAlgorithm.parse(json.getAsString()); - } else { - return null; - } - } - }) - .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") - .create(); + }) + .registerTypeAdapter(EncryptionMethod.class, new JsonDeserializer() { + @Override + public EncryptionMethod deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (json.isJsonPrimitive()) { + return EncryptionMethod.parse(json.getAsString()); + } else { + return null; + } + } + }) + .registerTypeAdapter(JWKSet.class, new JsonDeserializer() { + @Override + public JWKSet deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (json.isJsonObject()) { + try { + return JWKSet.parse(json.toString()); + } catch (ParseException e) { + return null; + } + } else { + return null; + } + } + }) + .registerTypeAdapter(JWT.class, new JsonDeserializer() { + @Override + public JWT deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (json.isJsonPrimitive()) { + try { + return JWTParser.parse(json.getAsString()); + } catch (ParseException e) { + return null; + } + } else { + return null; + } + } + }) + .registerTypeAdapter(PKCEAlgorithm.class, new JsonDeserializer() { + @Override + public PKCEAlgorithm deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (json.isJsonPrimitive()) { + return PKCEAlgorithm.parse(json.getAsString()); + } else { + return null; + } + } + }) + .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") + .create(); /** * Logger for this class @@ -508,37 +509,37 @@ public String apiShowClient(@PathVariable("id") Long id, Model model, Authentica * Get the logo image for a client * @param id */ - @RequestMapping(value = "/{id}/logo", method=RequestMethod.GET, produces = { MediaType.IMAGE_GIF_VALUE, MediaType.IMAGE_JPEG_VALUE, MediaType.IMAGE_PNG_VALUE }) - public ResponseEntity getClientLogo(@PathVariable("id") Long id, Model model) { + @RequestMapping(value = "/{id}/logo", method=RequestMethod.GET, produces = { MediaType.IMAGE_GIF_VALUE, MediaType.IMAGE_JPEG_VALUE, MediaType.IMAGE_PNG_VALUE }) + public ResponseEntity getClientLogo(@PathVariable("id") Long id, Model model) { - ClientDetailsEntity client = clientService.getClientById(id); + ClientDetailsEntity client = clientService.getClientById(id); - if (client == null) { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } else if (Strings.isNullOrEmpty(client.getLogoUri())) { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } else { - // get the image from cache - CachedImage image = clientLogoLoadingService.getLogo(client); + if (client == null) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } else if (Strings.isNullOrEmpty(client.getLogoUri())) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } else { + // get the image from cache + CachedImage image = clientLogoLoadingService.getLogo(client); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.parseMediaType(image.getContentType())); - headers.setContentLength(image.getLength()); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.parseMediaType(image.getContentType())); + headers.setContentLength(image.getLength()); - return new ResponseEntity<>(image.getData(), headers, HttpStatus.OK); - } - } - - private ClientDetailsEntity validateSoftwareStatement(ClientDetailsEntity newClient) throws ValidationException { - if (newClient.getSoftwareStatement() != null) { - if (assertionValidator.isValid(newClient.getSoftwareStatement())) { - // we have a software statement and its envelope passed all the checks from our validator - - // swap out all of the client's fields for the associated parts of the software statement - try { - JWTClaimsSet claimSet = newClient.getSoftwareStatement().getJWTClaimsSet(); - for (String claim : claimSet.getClaims().keySet()) { - switch (claim) { + return new ResponseEntity<>(image.getData(), headers, HttpStatus.OK); + } + } + + private ClientDetailsEntity validateSoftwareStatement(ClientDetailsEntity newClient) throws ValidationException { + if (newClient.getSoftwareStatement() != null) { + if (assertionValidator.isValid(newClient.getSoftwareStatement())) { + // we have a software statement and its envelope passed all the checks from our validator + + // swap out all of the client's fields for the associated parts of the software statement + try { + JWTClaimsSet claimSet = newClient.getSoftwareStatement().getJWTClaimsSet(); + for (String claim : claimSet.getClaims().keySet()) { + switch (claim) { case SOFTWARE_STATEMENT: throw new ValidationException("invalid_client_metadata", "Software statement can't include another software statement", HttpStatus.BAD_REQUEST); case CLAIMS_REDIRECT_URIS: @@ -610,7 +611,7 @@ private ClientDetailsEntity validateSoftwareStatement(ClientDetailsEntity newCli newClient.setJwks(JWKSet.parse(claimSet.getStringClaim(claim))); break; case POLICY_URI: - newClient.setPolicyUri(claimSet.getStringClaim(claim)); + newClient.setPolicyUri(claimSet.getStringClaim(claim)); break; case RESPONSE_TYPES: newClient.setResponseTypes(Sets.newHashSet(claimSet.getStringListClaim(claim))); @@ -650,21 +651,21 @@ private ClientDetailsEntity validateSoftwareStatement(ClientDetailsEntity newCli default: logger.warn("Software statement contained unknown field: " + claim + " with value " + claimSet.getClaim(claim)); break; - } } - - return newClient; - } catch (ParseException e) { - throw new ValidationException("invalid_client_metadata", "Software statement claims didn't parse", HttpStatus.BAD_REQUEST); } - } else { - throw new ValidationException("invalid_client_metadata", "Software statement rejected by validator", HttpStatus.BAD_REQUEST); + + return newClient; + } catch (ParseException e) { + throw new ValidationException("invalid_client_metadata", "Software statement claims didn't parse", HttpStatus.BAD_REQUEST); } } else { - // nothing to see here, carry on - return newClient; + throw new ValidationException("invalid_client_metadata", "Software statement rejected by validator", HttpStatus.BAD_REQUEST); } - + } else { + // nothing to see here, carry on + return newClient; } + } + } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java index 8f2667a3da..a95c5de96a 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DataAPI.java @@ -27,9 +27,6 @@ import org.mitre.openid.connect.config.ConfigurationPropertiesBean; import org.mitre.openid.connect.service.MITREidDataService; -import org.mitre.openid.connect.service.impl.MITREidDataService_1_0; -import org.mitre.openid.connect.service.impl.MITREidDataService_1_1; -import org.mitre.openid.connect.service.impl.MITREidDataService_1_2; import org.mitre.openid.connect.service.impl.MITREidDataService_1_3; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,9 +46,9 @@ /** * API endpoint for importing and exporting the current state of a server. * Includes all tokens, grants, whitelists, blacklists, and clients. - * + * * @author jricher - * + * */ @Controller @RequestMapping("/" + DataAPI.URL) @@ -72,13 +69,13 @@ public class DataAPI { @Autowired private List importers; - + private List supportedVersions = ImmutableList.of( - MITREidDataService.MITREID_CONNECT_1_0, - MITREidDataService.MITREID_CONNECT_1_1, - MITREidDataService.MITREID_CONNECT_1_2, - MITREidDataService.MITREID_CONNECT_1_3); - + MITREidDataService.MITREID_CONNECT_1_0, + MITREidDataService.MITREID_CONNECT_1_1, + MITREidDataService.MITREID_CONNECT_1_2, + MITREidDataService.MITREID_CONNECT_1_3); + @Autowired private MITREidDataService_1_3 exporter; @@ -92,31 +89,31 @@ public String importData(Reader in, Model m) throws IOException { while (reader.hasNext()) { JsonToken tok = reader.peek(); switch (tok) { - case NAME: - String name = reader.nextName(); - - if (supportedVersions.contains(name)) { - // we're working with a known data version tag - for (MITREidDataService dataService : importers) { - // dispatch to the correct service - if (dataService.supportsVersion(name)) { - dataService.importData(reader); - break; + case NAME: + String name = reader.nextName(); + + if (supportedVersions.contains(name)) { + // we're working with a known data version tag + for (MITREidDataService dataService : importers) { + // dispatch to the correct service + if (dataService.supportsVersion(name)) { + dataService.importData(reader); + break; + } } + } else { + // consume the next bit silently for now + logger.debug("Skipping value for " + name); // TODO: write these out? + reader.skipValue(); } - } else { - // consume the next bit silently for now - logger.debug("Skipping value for " + name); // TODO: write these out? - reader.skipValue(); - } - break; - case END_OBJECT: - break; - case END_DOCUMENT: - break; + break; + case END_OBJECT: + break; + case END_DOCUMENT: + break; } } - + reader.endObject(); return "httpCodeView"; diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DynamicClientRegistrationEndpoint.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DynamicClientRegistrationEndpoint.java index afa4b6d70b..f99e5cc669 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DynamicClientRegistrationEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DynamicClientRegistrationEndpoint.java @@ -16,8 +16,6 @@ *******************************************************************************/ package org.mitre.openid.connect.web; -import static org.mitre.oauth2.model.RegisteredClientFields.*; - import java.io.UnsupportedEncodingException; import java.text.ParseException; import java.util.Date; @@ -26,7 +24,6 @@ import java.util.concurrent.TimeUnit; import org.mitre.jwt.assertion.AssertionValidator; -import org.mitre.jwt.signer.service.JWTSigningAndValidationService; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.ClientDetailsEntity.AppType; import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; @@ -73,6 +70,45 @@ import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jwt.JWTClaimsSet; +import static org.mitre.oauth2.model.RegisteredClientFields.APPLICATION_TYPE; +import static org.mitre.oauth2.model.RegisteredClientFields.CLAIMS_REDIRECT_URIS; +import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_ID; +import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_ID_ISSUED_AT; +import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_NAME; +import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_SECRET; +import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_SECRET_EXPIRES_AT; +import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_URI; +import static org.mitre.oauth2.model.RegisteredClientFields.CONTACTS; +import static org.mitre.oauth2.model.RegisteredClientFields.DEFAULT_ACR_VALUES; +import static org.mitre.oauth2.model.RegisteredClientFields.DEFAULT_MAX_AGE; +import static org.mitre.oauth2.model.RegisteredClientFields.GRANT_TYPES; +import static org.mitre.oauth2.model.RegisteredClientFields.ID_TOKEN_ENCRYPTED_RESPONSE_ALG; +import static org.mitre.oauth2.model.RegisteredClientFields.ID_TOKEN_ENCRYPTED_RESPONSE_ENC; +import static org.mitre.oauth2.model.RegisteredClientFields.ID_TOKEN_SIGNED_RESPONSE_ALG; +import static org.mitre.oauth2.model.RegisteredClientFields.INITIATE_LOGIN_URI; +import static org.mitre.oauth2.model.RegisteredClientFields.JWKS; +import static org.mitre.oauth2.model.RegisteredClientFields.JWKS_URI; +import static org.mitre.oauth2.model.RegisteredClientFields.LOGO_URI; +import static org.mitre.oauth2.model.RegisteredClientFields.POLICY_URI; +import static org.mitre.oauth2.model.RegisteredClientFields.POST_LOGOUT_REDIRECT_URIS; +import static org.mitre.oauth2.model.RegisteredClientFields.REDIRECT_URIS; +import static org.mitre.oauth2.model.RegisteredClientFields.REGISTRATION_ACCESS_TOKEN; +import static org.mitre.oauth2.model.RegisteredClientFields.REGISTRATION_CLIENT_URI; +import static org.mitre.oauth2.model.RegisteredClientFields.REQUEST_OBJECT_SIGNING_ALG; +import static org.mitre.oauth2.model.RegisteredClientFields.REQUEST_URIS; +import static org.mitre.oauth2.model.RegisteredClientFields.REQUIRE_AUTH_TIME; +import static org.mitre.oauth2.model.RegisteredClientFields.RESPONSE_TYPES; +import static org.mitre.oauth2.model.RegisteredClientFields.SCOPE; +import static org.mitre.oauth2.model.RegisteredClientFields.SECTOR_IDENTIFIER_URI; +import static org.mitre.oauth2.model.RegisteredClientFields.SOFTWARE_STATEMENT; +import static org.mitre.oauth2.model.RegisteredClientFields.SUBJECT_TYPE; +import static org.mitre.oauth2.model.RegisteredClientFields.TOKEN_ENDPOINT_AUTH_METHOD; +import static org.mitre.oauth2.model.RegisteredClientFields.TOKEN_ENDPOINT_AUTH_SIGNING_ALG; +import static org.mitre.oauth2.model.RegisteredClientFields.TOS_URI; +import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_ENCRYPTED_RESPONSE_ALG; +import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_ENCRYPTED_RESPONSE_ENC; +import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_SIGNED_RESPONSE_ALG; + @Controller @RequestMapping(value = DynamicClientRegistrationEndpoint.URL) public class DynamicClientRegistrationEndpoint { @@ -85,9 +121,6 @@ public class DynamicClientRegistrationEndpoint { @Autowired private OAuth2TokenEntityService tokenService; - @Autowired - private JWTSigningAndValidationService jwtService; - @Autowired private SystemScopeService scopeService; @@ -575,132 +608,132 @@ private ClientDetailsEntity validateAuth(ClientDetailsEntity newClient) throws V /** * @param newClient * @return - * @throws ValidationException + * @throws ValidationException */ private ClientDetailsEntity validateSoftwareStatement(ClientDetailsEntity newClient) throws ValidationException { if (newClient.getSoftwareStatement() != null) { if (assertionValidator.isValid(newClient.getSoftwareStatement())) { // we have a software statement and its envelope passed all the checks from our validator - + // swap out all of the client's fields for the associated parts of the software statement try { JWTClaimsSet claimSet = newClient.getSoftwareStatement().getJWTClaimsSet(); for (String claim : claimSet.getClaims().keySet()) { switch (claim) { - case SOFTWARE_STATEMENT: - throw new ValidationException("invalid_client_metadata", "Software statement can't include another software statement", HttpStatus.BAD_REQUEST); - case CLAIMS_REDIRECT_URIS: - newClient.setClaimsRedirectUris(Sets.newHashSet(claimSet.getStringListClaim(claim))); - break; - case CLIENT_SECRET_EXPIRES_AT: - throw new ValidationException("invalid_client_metadata", "Software statement can't include a client secret expiration time", HttpStatus.BAD_REQUEST); - case CLIENT_ID_ISSUED_AT: - throw new ValidationException("invalid_client_metadata", "Software statement can't include a client ID issuance time", HttpStatus.BAD_REQUEST); - case REGISTRATION_CLIENT_URI: - throw new ValidationException("invalid_client_metadata", "Software statement can't include a client configuration endpoint", HttpStatus.BAD_REQUEST); - case REGISTRATION_ACCESS_TOKEN: - throw new ValidationException("invalid_client_metadata", "Software statement can't include a client registration access token", HttpStatus.BAD_REQUEST); - case REQUEST_URIS: - newClient.setRequestUris(Sets.newHashSet(claimSet.getStringListClaim(claim))); - break; - case POST_LOGOUT_REDIRECT_URIS: - newClient.setPostLogoutRedirectUris(Sets.newHashSet(claimSet.getStringListClaim(claim))); - break; - case INITIATE_LOGIN_URI: - newClient.setInitiateLoginUri(claimSet.getStringClaim(claim)); - break; - case DEFAULT_ACR_VALUES: - newClient.setDefaultACRvalues(Sets.newHashSet(claimSet.getStringListClaim(claim))); - break; - case REQUIRE_AUTH_TIME: - newClient.setRequireAuthTime(claimSet.getBooleanClaim(claim)); - break; - case DEFAULT_MAX_AGE: - newClient.setDefaultMaxAge(claimSet.getIntegerClaim(claim)); - break; - case TOKEN_ENDPOINT_AUTH_SIGNING_ALG: - newClient.setTokenEndpointAuthSigningAlg(JWSAlgorithm.parse(claimSet.getStringClaim(claim))); - break; - case ID_TOKEN_ENCRYPTED_RESPONSE_ENC: - newClient.setIdTokenEncryptedResponseEnc(EncryptionMethod.parse(claimSet.getStringClaim(claim))); - break; - case ID_TOKEN_ENCRYPTED_RESPONSE_ALG: - newClient.setIdTokenEncryptedResponseAlg(JWEAlgorithm.parse(claimSet.getStringClaim(claim))); - break; - case ID_TOKEN_SIGNED_RESPONSE_ALG: - newClient.setIdTokenSignedResponseAlg(JWSAlgorithm.parse(claimSet.getStringClaim(claim))); - break; - case USERINFO_ENCRYPTED_RESPONSE_ENC: - newClient.setUserInfoEncryptedResponseEnc(EncryptionMethod.parse(claimSet.getStringClaim(claim))); - break; - case USERINFO_ENCRYPTED_RESPONSE_ALG: - newClient.setUserInfoEncryptedResponseAlg(JWEAlgorithm.parse(claimSet.getStringClaim(claim))); - break; - case USERINFO_SIGNED_RESPONSE_ALG: - newClient.setUserInfoSignedResponseAlg(JWSAlgorithm.parse(claimSet.getStringClaim(claim))); - break; - case REQUEST_OBJECT_SIGNING_ALG: - newClient.setRequestObjectSigningAlg(JWSAlgorithm.parse(claimSet.getStringClaim(claim))); - break; - case SUBJECT_TYPE: - newClient.setSubjectType(SubjectType.getByValue(claimSet.getStringClaim(claim))); - break; - case SECTOR_IDENTIFIER_URI: - newClient.setSectorIdentifierUri(claimSet.getStringClaim(claim)); - break; - case APPLICATION_TYPE: - newClient.setApplicationType(AppType.getByValue(claimSet.getStringClaim(claim))); - break; - case JWKS_URI: - newClient.setJwksUri(claimSet.getStringClaim(claim)); - break; - case JWKS: - newClient.setJwks(JWKSet.parse(claimSet.getStringClaim(claim))); - break; - case POLICY_URI: - newClient.setPolicyUri(claimSet.getStringClaim(claim)); - break; - case RESPONSE_TYPES: - newClient.setResponseTypes(Sets.newHashSet(claimSet.getStringListClaim(claim))); - break; - case GRANT_TYPES: - newClient.setGrantTypes(Sets.newHashSet(claimSet.getStringListClaim(claim))); - break; - case SCOPE: - newClient.setScope(OAuth2Utils.parseParameterList(claimSet.getStringClaim(claim))); - break; - case TOKEN_ENDPOINT_AUTH_METHOD: - newClient.setTokenEndpointAuthMethod(AuthMethod.getByValue(claimSet.getStringClaim(claim))); - break; - case TOS_URI: - newClient.setTosUri(claimSet.getStringClaim(claim)); - break; - case CONTACTS: - newClient.setContacts(Sets.newHashSet(claimSet.getStringListClaim(claim))); - break; - case LOGO_URI: - newClient.setLogoUri(claimSet.getStringClaim(claim)); - break; - case CLIENT_URI: - newClient.setClientUri(claimSet.getStringClaim(claim)); - break; - case CLIENT_NAME: - newClient.setClientName(claimSet.getStringClaim(claim)); - break; - case REDIRECT_URIS: - newClient.setRedirectUris(Sets.newHashSet(claimSet.getStringListClaim(claim))); - break; - case CLIENT_SECRET: - throw new ValidationException("invalid_client_metadata", "Software statement can't contain client secret", HttpStatus.BAD_REQUEST); - case CLIENT_ID: - throw new ValidationException("invalid_client_metadata", "Software statement can't contain client ID", HttpStatus.BAD_REQUEST); - - default: - logger.warn("Software statement contained unknown field: " + claim + " with value " + claimSet.getClaim(claim)); - break; + case SOFTWARE_STATEMENT: + throw new ValidationException("invalid_client_metadata", "Software statement can't include another software statement", HttpStatus.BAD_REQUEST); + case CLAIMS_REDIRECT_URIS: + newClient.setClaimsRedirectUris(Sets.newHashSet(claimSet.getStringListClaim(claim))); + break; + case CLIENT_SECRET_EXPIRES_AT: + throw new ValidationException("invalid_client_metadata", "Software statement can't include a client secret expiration time", HttpStatus.BAD_REQUEST); + case CLIENT_ID_ISSUED_AT: + throw new ValidationException("invalid_client_metadata", "Software statement can't include a client ID issuance time", HttpStatus.BAD_REQUEST); + case REGISTRATION_CLIENT_URI: + throw new ValidationException("invalid_client_metadata", "Software statement can't include a client configuration endpoint", HttpStatus.BAD_REQUEST); + case REGISTRATION_ACCESS_TOKEN: + throw new ValidationException("invalid_client_metadata", "Software statement can't include a client registration access token", HttpStatus.BAD_REQUEST); + case REQUEST_URIS: + newClient.setRequestUris(Sets.newHashSet(claimSet.getStringListClaim(claim))); + break; + case POST_LOGOUT_REDIRECT_URIS: + newClient.setPostLogoutRedirectUris(Sets.newHashSet(claimSet.getStringListClaim(claim))); + break; + case INITIATE_LOGIN_URI: + newClient.setInitiateLoginUri(claimSet.getStringClaim(claim)); + break; + case DEFAULT_ACR_VALUES: + newClient.setDefaultACRvalues(Sets.newHashSet(claimSet.getStringListClaim(claim))); + break; + case REQUIRE_AUTH_TIME: + newClient.setRequireAuthTime(claimSet.getBooleanClaim(claim)); + break; + case DEFAULT_MAX_AGE: + newClient.setDefaultMaxAge(claimSet.getIntegerClaim(claim)); + break; + case TOKEN_ENDPOINT_AUTH_SIGNING_ALG: + newClient.setTokenEndpointAuthSigningAlg(JWSAlgorithm.parse(claimSet.getStringClaim(claim))); + break; + case ID_TOKEN_ENCRYPTED_RESPONSE_ENC: + newClient.setIdTokenEncryptedResponseEnc(EncryptionMethod.parse(claimSet.getStringClaim(claim))); + break; + case ID_TOKEN_ENCRYPTED_RESPONSE_ALG: + newClient.setIdTokenEncryptedResponseAlg(JWEAlgorithm.parse(claimSet.getStringClaim(claim))); + break; + case ID_TOKEN_SIGNED_RESPONSE_ALG: + newClient.setIdTokenSignedResponseAlg(JWSAlgorithm.parse(claimSet.getStringClaim(claim))); + break; + case USERINFO_ENCRYPTED_RESPONSE_ENC: + newClient.setUserInfoEncryptedResponseEnc(EncryptionMethod.parse(claimSet.getStringClaim(claim))); + break; + case USERINFO_ENCRYPTED_RESPONSE_ALG: + newClient.setUserInfoEncryptedResponseAlg(JWEAlgorithm.parse(claimSet.getStringClaim(claim))); + break; + case USERINFO_SIGNED_RESPONSE_ALG: + newClient.setUserInfoSignedResponseAlg(JWSAlgorithm.parse(claimSet.getStringClaim(claim))); + break; + case REQUEST_OBJECT_SIGNING_ALG: + newClient.setRequestObjectSigningAlg(JWSAlgorithm.parse(claimSet.getStringClaim(claim))); + break; + case SUBJECT_TYPE: + newClient.setSubjectType(SubjectType.getByValue(claimSet.getStringClaim(claim))); + break; + case SECTOR_IDENTIFIER_URI: + newClient.setSectorIdentifierUri(claimSet.getStringClaim(claim)); + break; + case APPLICATION_TYPE: + newClient.setApplicationType(AppType.getByValue(claimSet.getStringClaim(claim))); + break; + case JWKS_URI: + newClient.setJwksUri(claimSet.getStringClaim(claim)); + break; + case JWKS: + newClient.setJwks(JWKSet.parse(claimSet.getStringClaim(claim))); + break; + case POLICY_URI: + newClient.setPolicyUri(claimSet.getStringClaim(claim)); + break; + case RESPONSE_TYPES: + newClient.setResponseTypes(Sets.newHashSet(claimSet.getStringListClaim(claim))); + break; + case GRANT_TYPES: + newClient.setGrantTypes(Sets.newHashSet(claimSet.getStringListClaim(claim))); + break; + case SCOPE: + newClient.setScope(OAuth2Utils.parseParameterList(claimSet.getStringClaim(claim))); + break; + case TOKEN_ENDPOINT_AUTH_METHOD: + newClient.setTokenEndpointAuthMethod(AuthMethod.getByValue(claimSet.getStringClaim(claim))); + break; + case TOS_URI: + newClient.setTosUri(claimSet.getStringClaim(claim)); + break; + case CONTACTS: + newClient.setContacts(Sets.newHashSet(claimSet.getStringListClaim(claim))); + break; + case LOGO_URI: + newClient.setLogoUri(claimSet.getStringClaim(claim)); + break; + case CLIENT_URI: + newClient.setClientUri(claimSet.getStringClaim(claim)); + break; + case CLIENT_NAME: + newClient.setClientName(claimSet.getStringClaim(claim)); + break; + case REDIRECT_URIS: + newClient.setRedirectUris(Sets.newHashSet(claimSet.getStringListClaim(claim))); + break; + case CLIENT_SECRET: + throw new ValidationException("invalid_client_metadata", "Software statement can't contain client secret", HttpStatus.BAD_REQUEST); + case CLIENT_ID: + throw new ValidationException("invalid_client_metadata", "Software statement can't contain client ID", HttpStatus.BAD_REQUEST); + + default: + logger.warn("Software statement contained unknown field: " + claim + " with value " + claimSet.getClaim(claim)); + break; } } - + return newClient; } catch (ParseException e) { throw new ValidationException("invalid_client_metadata", "Software statement claims didn't parse", HttpStatus.BAD_REQUEST); @@ -712,10 +745,10 @@ private ClientDetailsEntity validateSoftwareStatement(ClientDetailsEntity newCli // nothing to see here, carry on return newClient; } - + } - + /* * Rotates the registration token if it's expired, otherwise returns it */ diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ProtectedResourceRegistrationEndpoint.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ProtectedResourceRegistrationEndpoint.java index 938957af0a..947dfe092f 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ProtectedResourceRegistrationEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ProtectedResourceRegistrationEndpoint.java @@ -22,7 +22,6 @@ import java.util.HashSet; import java.util.Set; -import org.mitre.jwt.signer.service.JWTSigningAndValidationService; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; import org.mitre.oauth2.model.OAuth2AccessTokenEntity; @@ -34,7 +33,6 @@ import org.mitre.openid.connect.ClientDetailsEntityJsonProcessor; import org.mitre.openid.connect.config.ConfigurationPropertiesBean; import org.mitre.openid.connect.exception.ValidationException; -import org.mitre.openid.connect.service.BlacklistedSiteService; import org.mitre.openid.connect.service.OIDCTokenService; import org.mitre.openid.connect.view.ClientInformationResponseView; import org.mitre.openid.connect.view.HttpCodeView; @@ -63,7 +61,7 @@ public class ProtectedResourceRegistrationEndpoint { /** - * + * */ public static final String URL = "resource"; @@ -73,15 +71,9 @@ public class ProtectedResourceRegistrationEndpoint { @Autowired private OAuth2TokenEntityService tokenService; - @Autowired - private JWTSigningAndValidationService jwtService; - @Autowired private SystemScopeService scopeService; - @Autowired - private BlacklistedSiteService blacklistService; - @Autowired private ConfigurationPropertiesBean config; diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ServerConfigInterceptor.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ServerConfigInterceptor.java index 321d7b7633..812bbd69d4 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ServerConfigInterceptor.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ServerConfigInterceptor.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.web; @@ -28,10 +28,10 @@ import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; /** - * + * * Injects the server configuration bean into the request context. * This allows JSPs and the like to call "config.logoUrl" among others. - * + * * @author jricher * */ @@ -39,7 +39,7 @@ public class ServerConfigInterceptor extends HandlerInterceptorAdapter { @Autowired private ConfigurationPropertiesBean config; - + @Autowired private UIConfiguration ui; diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/StatsAPI.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/StatsAPI.java index 7bbb44c711..4f5ae9b14b 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/StatsAPI.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/StatsAPI.java @@ -54,16 +54,16 @@ public String statsSummary(ModelMap m) { } -// @PreAuthorize("hasRole('ROLE_USER')") -// @RequestMapping(value = "byclientid", produces = MediaType.APPLICATION_JSON_VALUE) -// public String statsByClient(ModelMap m) { -// Map e = statsService.getByClientId(); -// -// m.put(JsonEntityView.ENTITY, e); -// -// return JsonEntityView.VIEWNAME; -// } -// + // @PreAuthorize("hasRole('ROLE_USER')") + // @RequestMapping(value = "byclientid", produces = MediaType.APPLICATION_JSON_VALUE) + // public String statsByClient(ModelMap m) { + // Map e = statsService.getByClientId(); + // + // m.put(JsonEntityView.ENTITY, e); + // + // return JsonEntityView.VIEWNAME; + // } + // @PreAuthorize("hasRole('ROLE_USER')") @RequestMapping(value = "byclientid/{id}", produces = MediaType.APPLICATION_JSON_VALUE) public String statsByClientId(@PathVariable("id") String clientId, ModelMap m) { diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java index 19d01c6280..a50a38abf2 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java @@ -45,7 +45,7 @@ /** * OpenID Connect UserInfo endpoint, as specified in Standard sec 5 and Messages sec 2.4. - * + * * @author AANGANES * */ diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/WhitelistAPI.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/WhitelistAPI.java index f9196b01ff..5cd0a723c0 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/WhitelistAPI.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/WhitelistAPI.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.web; @@ -168,7 +168,7 @@ public String updateWhitelistedSite(@PathVariable("id") Long id, @RequestBody St /** * Delete a whitelisted site - * + * */ @PreAuthorize("hasRole('ROLE_ADMIN')") @RequestMapping(value="/{id}", method = RequestMethod.DELETE) diff --git a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestBlacklistAwareRedirectResolver.java b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestBlacklistAwareRedirectResolver.java index 16497e36ab..40daba48c3 100644 --- a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestBlacklistAwareRedirectResolver.java +++ b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestBlacklistAwareRedirectResolver.java @@ -17,6 +17,8 @@ package org.mitre.oauth2.service.impl; +import static org.mockito.Matchers.anyString; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -33,8 +35,6 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; -import static org.mockito.Matchers.anyString; - import static org.mockito.Mockito.when; import static org.junit.Assert.assertThat; @@ -54,7 +54,7 @@ public class TestBlacklistAwareRedirectResolver { @Mock private ConfigurationPropertiesBean config; - + @InjectMocks private BlacklistAwareRedirectResolver resolver; @@ -133,7 +133,7 @@ public void testRedirectMatches_default() { assertThat(res2, is(true)); } - + @Test public void testHeartMode() { when(config.isHeartMode()).thenReturn(true); diff --git a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java index dba6bb1b4a..29e63039bb 100644 --- a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java +++ b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java @@ -16,6 +16,9 @@ *******************************************************************************/ package org.mitre.oauth2.service.impl; +import static com.google.common.collect.Sets.newHashSet; +import static org.mockito.BDDMockito.given; + import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; @@ -38,13 +41,9 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import static com.google.common.collect.Sets.newHashSet; - import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; -import static org.mockito.BDDMockito.given; - import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; @@ -61,7 +60,7 @@ public void shouldAssembleExpectedResultForAccessToken() throws ParseException { // given OAuth2AccessTokenEntity accessToken = accessToken(new Date(123 * 1000L), scopes("foo", "bar"), null, "Bearer", - oauth2AuthenticationWithUser(oauth2Request("clientId"), "name")); + oauth2AuthenticationWithUser(oauth2Request("clientId"), "name")); UserInfo userInfo = userInfo("sub"); @@ -91,7 +90,7 @@ public void shouldAssembleExpectedResultForAccessToken_withPermissions() throws // given OAuth2AccessTokenEntity accessToken = accessToken(new Date(123 * 1000L), scopes("foo", "bar"), permissions(permission(1L, "foo", "bar")), - "Bearer", oauth2AuthenticationWithUser(oauth2Request("clientId"), "name")); + "Bearer", oauth2AuthenticationWithUser(oauth2Request("clientId"), "name")); UserInfo userInfo = userInfo("sub"); @@ -156,7 +155,7 @@ public void shouldAssembleExpectedResultForAccessTokenWithoutExpiry() { // given OAuth2AccessTokenEntity accessToken = accessToken(null, scopes("foo", "bar"), null, "Bearer", - oauth2AuthenticationWithUser(oauth2Request("clientId"), "name")); + oauth2AuthenticationWithUser(oauth2Request("clientId"), "name")); UserInfo userInfo = userInfo("sub"); @@ -179,36 +178,36 @@ public void shouldAssembleExpectedResultForAccessTokenWithoutExpiry() { } @Test - public void shouldAssembleExpectedResultForAccessTokenWithoutUserAuthentication() throws ParseException { - // given - OAuth2AccessTokenEntity accessToken = accessToken(new Date(123 * 1000L), scopes("foo", "bar"), null, "Bearer", - oauth2Authentication(oauth2Request("clientId"), null)); - - Set authScopes = scopes("foo", "bar", "baz"); - - // when - Map result = assembler.assembleFrom(accessToken, null, authScopes); - - - // then `user_id` should not be present - Map expected = new ImmutableMap.Builder() - .put("sub", "clientId") - .put("exp", 123L) - .put("expires_at", dateFormat.valueToString(new Date(123 * 1000L))) - .put("scope", "bar foo") - .put("active", Boolean.TRUE) - .put("client_id", "clientId") - .put("token_type", "Bearer") - .build(); - assertThat(result, is(equalTo(expected))); - } + public void shouldAssembleExpectedResultForAccessTokenWithoutUserAuthentication() throws ParseException { + // given + OAuth2AccessTokenEntity accessToken = accessToken(new Date(123 * 1000L), scopes("foo", "bar"), null, "Bearer", + oauth2Authentication(oauth2Request("clientId"), null)); + + Set authScopes = scopes("foo", "bar", "baz"); + + // when + Map result = assembler.assembleFrom(accessToken, null, authScopes); + + + // then `user_id` should not be present + Map expected = new ImmutableMap.Builder() + .put("sub", "clientId") + .put("exp", 123L) + .put("expires_at", dateFormat.valueToString(new Date(123 * 1000L))) + .put("scope", "bar foo") + .put("active", Boolean.TRUE) + .put("client_id", "clientId") + .put("token_type", "Bearer") + .build(); + assertThat(result, is(equalTo(expected))); + } @Test public void shouldAssembleExpectedResultForRefreshToken() throws ParseException { // given OAuth2RefreshTokenEntity refreshToken = refreshToken(new Date(123 * 1000L), - oauth2AuthenticationWithUser(oauth2Request("clientId", scopes("foo", "bar")), "name")); + oauth2AuthenticationWithUser(oauth2Request("clientId", scopes("foo", "bar")), "name")); UserInfo userInfo = userInfo("sub"); @@ -283,27 +282,27 @@ public void shouldAssembleExpectedResultForRefreshTokenWithoutExpiry() { assertThat(result, is(equalTo(expected))); } - @Test - public void shouldAssembleExpectedResultForRefreshTokenWithoutUserAuthentication() throws ParseException { - // given - OAuth2RefreshTokenEntity refreshToken = refreshToken(null, - oauth2Authentication(oauth2Request("clientId", scopes("foo", "bar")), null)); + @Test + public void shouldAssembleExpectedResultForRefreshTokenWithoutUserAuthentication() throws ParseException { + // given + OAuth2RefreshTokenEntity refreshToken = refreshToken(null, + oauth2Authentication(oauth2Request("clientId", scopes("foo", "bar")), null)); - Set authScopes = scopes("foo", "bar", "baz"); + Set authScopes = scopes("foo", "bar", "baz"); - // when - Map result = assembler.assembleFrom(refreshToken, null, authScopes); + // when + Map result = assembler.assembleFrom(refreshToken, null, authScopes); - // then `user_id` should not be present - Map expected = new ImmutableMap.Builder() - .put("sub", "clientId") - .put("scope", "bar foo") - .put("active", Boolean.TRUE) - .put("client_id", "clientId") - .build(); - assertThat(result, is(equalTo(expected))); - } + // then `user_id` should not be present + Map expected = new ImmutableMap.Builder() + .put("sub", "clientId") + .put("scope", "bar foo") + .put("active", Boolean.TRUE) + .put("client_id", "clientId") + .build(); + assertThat(result, is(equalTo(expected))); + } @@ -332,12 +331,12 @@ private OAuth2RefreshTokenEntity refreshToken(Date exp, OAuth2Authentication aut private OAuth2Authentication oauth2AuthenticationWithUser(OAuth2Request request, String username) { UsernamePasswordAuthenticationToken userAuthentication = new UsernamePasswordAuthenticationToken(username, "somepassword"); - return oauth2Authentication(request, userAuthentication); + return oauth2Authentication(request, userAuthentication); } - private OAuth2Authentication oauth2Authentication(OAuth2Request request, Authentication userAuthentication) { - return new OAuth2Authentication(request, userAuthentication); - } + private OAuth2Authentication oauth2Authentication(OAuth2Request request, Authentication userAuthentication) { + return new OAuth2Authentication(request, userAuthentication); + } private OAuth2Request oauth2Request(String clientId) { return oauth2Request(clientId, null); diff --git a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultOAuth2ClientDetailsEntityService.java b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultOAuth2ClientDetailsEntityService.java index fabe080ca0..40bdcd496c 100644 --- a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultOAuth2ClientDetailsEntityService.java +++ b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultOAuth2ClientDetailsEntityService.java @@ -16,8 +16,6 @@ *******************************************************************************/ package org.mitre.oauth2.service.impl; -import static org.mockito.Matchers.anyString; - import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; @@ -51,7 +49,6 @@ import com.google.common.collect.Sets; -import static org.hamcrest.CoreMatchers.any; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; @@ -90,7 +87,7 @@ public class TestDefaultOAuth2ClientDetailsEntityService { @Mock private StatsService statsService; - + @Mock private ConfigurationPropertiesBean config; @@ -145,7 +142,7 @@ public Set answer(InvocationOnMock invocation) throws Throwable { // we're not testing reserved scopes here, just pass through when it's called Mockito.when(scopeService.removeReservedScopes(Matchers.anySet())).then(AdditionalAnswers.returnsFirstArg()); - + Mockito.when(config.isHeartMode()).thenReturn(false); } @@ -365,7 +362,7 @@ public void updateClient_noOfflineAccess() { assertThat(client.getScope().contains(SystemScopeService.OFFLINE_ACCESS), is(equalTo(false))); } - + @Test(expected = IllegalArgumentException.class) public void heartMode_authcode_invalidGrants() { Mockito.when(config.isHeartMode()).thenReturn(true); @@ -378,15 +375,15 @@ public void heartMode_authcode_invalidGrants() { client.setGrantTypes(grantTypes); client.setTokenEndpointAuthMethod(AuthMethod.PRIVATE_KEY); - + client.setRedirectUris(Sets.newHashSet("https://foo.bar/")); client.setJwksUri("https://foo.bar/jwks"); - + service.saveNewClient(client); - + } - + @Test(expected = IllegalArgumentException.class) public void heartMode_implicit_invalidGrants() { Mockito.when(config.isHeartMode()).thenReturn(true); @@ -399,13 +396,13 @@ public void heartMode_implicit_invalidGrants() { client.setGrantTypes(grantTypes); client.setTokenEndpointAuthMethod(AuthMethod.NONE); - + client.setRedirectUris(Sets.newHashSet("https://foo.bar/")); client.setJwksUri("https://foo.bar/jwks"); - + service.saveNewClient(client); - + } @Test(expected = IllegalArgumentException.class) @@ -420,11 +417,11 @@ public void heartMode_clientcreds_invalidGrants() { client.setGrantTypes(grantTypes); client.setTokenEndpointAuthMethod(AuthMethod.PRIVATE_KEY); - + client.setJwksUri("https://foo.bar/jwks"); - + service.saveNewClient(client); - + } @Test(expected = IllegalArgumentException.class) @@ -435,17 +432,17 @@ public void heartMode_authcode_authMethod() { Set grantTypes = new LinkedHashSet<>(); grantTypes.add("authorization_code"); client.setGrantTypes(grantTypes); - + client.setTokenEndpointAuthMethod(AuthMethod.SECRET_POST); client.setRedirectUris(Sets.newHashSet("https://foo.bar/")); client.setJwksUri("https://foo.bar/jwks"); - + service.saveNewClient(client); - + } - + @Test(expected = IllegalArgumentException.class) public void heartMode_implicit_authMethod() { Mockito.when(config.isHeartMode()).thenReturn(true); @@ -460,9 +457,9 @@ public void heartMode_implicit_authMethod() { client.setRedirectUris(Sets.newHashSet("https://foo.bar/")); client.setJwksUri("https://foo.bar/jwks"); - + service.saveNewClient(client); - + } @Test(expected = IllegalArgumentException.class) @@ -475,13 +472,13 @@ public void heartMode_clientcreds_authMethod() { client.setGrantTypes(grantTypes); client.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC); - + client.setRedirectUris(Sets.newHashSet("https://foo.bar/")); client.setJwksUri("https://foo.bar/jwks"); service.saveNewClient(client); - + } @Test(expected = IllegalArgumentException.class) @@ -492,13 +489,13 @@ public void heartMode_authcode_redirectUris() { Set grantTypes = new LinkedHashSet<>(); grantTypes.add("authorization_code"); client.setGrantTypes(grantTypes); - + client.setTokenEndpointAuthMethod(AuthMethod.PRIVATE_KEY); service.saveNewClient(client); - + } - + @Test(expected = IllegalArgumentException.class) public void heartMode_implicit_redirectUris() { Mockito.when(config.isHeartMode()).thenReturn(true); @@ -511,7 +508,7 @@ public void heartMode_implicit_redirectUris() { client.setTokenEndpointAuthMethod(AuthMethod.NONE); service.saveNewClient(client); - + } @Test(expected = IllegalArgumentException.class) @@ -528,7 +525,7 @@ public void heartMode_clientcreds_redirectUris() { client.setRedirectUris(Sets.newHashSet("http://foo.bar/")); service.saveNewClient(client); - + } @Test(expected = IllegalArgumentException.class) @@ -539,15 +536,15 @@ public void heartMode_clientSecret() { Set grantTypes = new LinkedHashSet<>(); grantTypes.add("authorization_code"); client.setGrantTypes(grantTypes); - + client.setTokenEndpointAuthMethod(AuthMethod.PRIVATE_KEY); - + client.setRedirectUris(Sets.newHashSet("http://foo.bar/")); client.setClientSecret("secret!"); - + service.saveNewClient(client); - + } @Test(expected = IllegalArgumentException.class) @@ -558,18 +555,18 @@ public void heartMode_noJwks() { Set grantTypes = new LinkedHashSet<>(); grantTypes.add("authorization_code"); client.setGrantTypes(grantTypes); - + client.setTokenEndpointAuthMethod(AuthMethod.PRIVATE_KEY); - + client.setRedirectUris(Sets.newHashSet("https://foo.bar/")); client.setJwks(null); client.setJwksUri(null); - + service.saveNewClient(client); - + } - + @Test public void heartMode_validAuthcodeClient() { Mockito.when(config.isHeartMode()).thenReturn(true); @@ -579,19 +576,19 @@ public void heartMode_validAuthcodeClient() { grantTypes.add("authorization_code"); grantTypes.add("refresh_token"); client.setGrantTypes(grantTypes); - + client.setTokenEndpointAuthMethod(AuthMethod.PRIVATE_KEY); - + client.setRedirectUris(Sets.newHashSet("https://foo.bar/")); client.setJwksUri("https://foo.bar/jwks"); - + service.saveNewClient(client); - + assertThat(client.getClientId(), is(notNullValue(String.class))); assertThat(client.getClientSecret(), is(nullValue())); } - + @Test(expected = IllegalArgumentException.class) public void heartMode_nonLocalHttpRedirect() { Mockito.when(config.isHeartMode()).thenReturn(true); @@ -601,17 +598,17 @@ public void heartMode_nonLocalHttpRedirect() { grantTypes.add("authorization_code"); grantTypes.add("refresh_token"); client.setGrantTypes(grantTypes); - + client.setTokenEndpointAuthMethod(AuthMethod.PRIVATE_KEY); - + client.setRedirectUris(Sets.newHashSet("http://foo.bar/")); client.setJwksUri("https://foo.bar/jwks"); - + service.saveNewClient(client); - + } - + @Test(expected = IllegalArgumentException.class) public void heartMode_multipleRedirectClass() { Mockito.when(config.isHeartMode()).thenReturn(true); @@ -621,14 +618,14 @@ public void heartMode_multipleRedirectClass() { grantTypes.add("authorization_code"); grantTypes.add("refresh_token"); client.setGrantTypes(grantTypes); - + client.setTokenEndpointAuthMethod(AuthMethod.PRIVATE_KEY); - + client.setRedirectUris(Sets.newHashSet("http://localhost/", "https://foo.bar", "foo://bar")); client.setJwksUri("https://foo.bar/jwks"); - + service.saveNewClient(client); - + } } diff --git a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultSystemScopeService.java b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultSystemScopeService.java index e78516b4d2..1643ec4c5d 100644 --- a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultSystemScopeService.java +++ b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultSystemScopeService.java @@ -26,9 +26,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; import org.mockito.runners.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; import com.google.common.collect.Sets; diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultApprovedSiteService.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultApprovedSiteService.java index 262599a8aa..5b9b4058ea 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultApprovedSiteService.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultApprovedSiteService.java @@ -16,7 +16,8 @@ *******************************************************************************/ package org.mitre.openid.connect.service.impl; -import java.util.ArrayList; +import static org.mockito.Matchers.any; + import java.util.HashSet; import java.util.List; import java.util.Set; @@ -40,8 +41,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; -import static org.mockito.Matchers.any; - import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -60,7 +59,7 @@ public class TestDefaultApprovedSiteService { @Mock private OAuth2TokenRepository tokenRepository; - + @Mock private StatsService statsService; diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultStatsService.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultStatsService.java index 930e659558..b8d19f2351 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultStatsService.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultStatsService.java @@ -23,7 +23,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mitre.oauth2.model.ClientDetailsEntity; -import org.mitre.oauth2.service.ClientDetailsEntityService; import org.mitre.openid.connect.model.ApprovedSite; import org.mitre.openid.connect.service.ApprovedSiteService; import org.mockito.InjectMocks; diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultUserInfoService.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultUserInfoService.java index 6808180747..628b28892c 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultUserInfoService.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestDefaultUserInfoService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.service.impl; diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java index d3e88207e8..96fdf2ad74 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java @@ -16,6 +16,11 @@ *******************************************************************************/ package org.mitre.openid.connect.service.impl; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.isA; +import static org.mockito.Matchers.isNull; + import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; @@ -73,11 +78,6 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.isA; -import static org.mockito.Matchers.isNull; - import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java index ccdde67edc..cb389a3d9a 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java @@ -16,6 +16,11 @@ *******************************************************************************/ package org.mitre.openid.connect.service.impl; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.isA; +import static org.mockito.Matchers.isNull; + import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; @@ -72,11 +77,6 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.isA; -import static org.mockito.Matchers.isNull; - import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java index b7c0a2fd0b..240e7fa8e4 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java @@ -16,9 +16,13 @@ *******************************************************************************/ package org.mitre.openid.connect.service.impl; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.isA; +import static org.mockito.Matchers.isNull; + import java.io.IOException; import java.io.StringReader; -import java.io.StringWriter; import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; @@ -62,42 +66,27 @@ import org.slf4j.LoggerFactory; import org.springframework.format.annotation.DateTimeFormat.ISO; import org.springframework.format.datetime.DateFormatter; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.gson.JsonArray; import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; import com.nimbusds.jwt.JWTParser; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.isA; -import static org.mockito.Matchers.isNull; - import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; @RunWith(MockitoJUnitRunner.class) @SuppressWarnings(value = {"rawtypes", "unchecked"}) diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java index 762b9ff695..d84bbfaef6 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java @@ -16,6 +16,11 @@ *******************************************************************************/ package org.mitre.openid.connect.service.impl; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.isA; +import static org.mockito.Matchers.isNull; + import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; @@ -83,18 +88,12 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.isA; -import static org.mockito.Matchers.isNull; - import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -1147,7 +1146,7 @@ public void testExportGrants() throws IOException, ParseException { site1.setUserId("user1"); site1.setAllowedScopes(ImmutableSet.of("openid", "phone")); when(mockToken1.getApprovedSite()).thenReturn(site1); - + Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+0000", Locale.ENGLISH); Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+0000", Locale.ENGLISH); Date timeoutDate2 = formatter.parse("2014-10-01T20:49:44.090+0000", Locale.ENGLISH); diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestUUIDPairwiseIdentiferService.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestUUIDPairwiseIdentiferService.java index ed35d82af3..734ddac3b7 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestUUIDPairwiseIdentiferService.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestUUIDPairwiseIdentiferService.java @@ -15,7 +15,7 @@ * limitations under the License. *******************************************************************************/ /** - * + * */ package org.mitre.openid.connect.service.impl; diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/util/TestIdTokenHashUtils.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/util/TestIdTokenHashUtils.java index f828135fd8..c9fec59d2b 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/util/TestIdTokenHashUtils.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/util/TestIdTokenHashUtils.java @@ -34,7 +34,7 @@ import static org.junit.Assert.assertEquals; /** - * + * * @author wkim * */ diff --git a/uma-server/src/main/java/org/mitre/uma/service/impl/MatchAllClaimsOnAnyPolicy.java b/uma-server/src/main/java/org/mitre/uma/service/impl/MatchAllClaimsOnAnyPolicy.java index 1759a8af31..1c180d5125 100644 --- a/uma-server/src/main/java/org/mitre/uma/service/impl/MatchAllClaimsOnAnyPolicy.java +++ b/uma-server/src/main/java/org/mitre/uma/service/impl/MatchAllClaimsOnAnyPolicy.java @@ -31,7 +31,7 @@ /** * Tests if all the claims in the required set have a matching * value in the supplied set. - * + * * @author jricher * */ diff --git a/uma-server/src/main/java/org/mitre/uma/service/impl/UmaDataServiceExtension_1_3.java b/uma-server/src/main/java/org/mitre/uma/service/impl/UmaDataServiceExtension_1_3.java index dc58922c34..ee7eeb09d5 100644 --- a/uma-server/src/main/java/org/mitre/uma/service/impl/UmaDataServiceExtension_1_3.java +++ b/uma-server/src/main/java/org/mitre/uma/service/impl/UmaDataServiceExtension_1_3.java @@ -59,74 +59,14 @@ */ @Service("umaDataExtension_1_3") public class UmaDataServiceExtension_1_3 extends MITREidDataServiceSupport implements MITREidDataServiceExtension { - + private static final String THIS_VERSION = MITREidDataService.MITREID_CONNECT_1_3; private static final String REGISTERED_CLIENT = "registeredClient"; - private static final String DEFAULT_SCOPE = "defaultScope"; - private static final String STRUCTURED_PARAMETER = "structuredParameter"; - private static final String STRUCTURED = "structured"; - private static final String RESTRICTED = "restricted"; - private static final String ICON = "icon"; - private static final String DYNAMICALLY_REGISTERED = "dynamicallyRegistered"; - private static final String CLEAR_ACCESS_TOKENS_ON_REFRESH = "clearAccessTokensOnRefresh"; - private static final String REUSE_REFRESH_TOKEN = "reuseRefreshToken"; - private static final String ALLOW_INTROSPECTION = "allowIntrospection"; - private static final String DESCRIPTION = "description"; - private static final String REQUEST_URIS = "requestUris"; - private static final String POST_LOGOUT_REDIRECT_URI = "postLogoutRedirectUri"; - private static final String INTITATE_LOGIN_URI = "intitateLoginUri"; - private static final String DEFAULT_ACR_VALUES = "defaultACRValues"; - private static final String REQUIRE_AUTH_TIME = "requireAuthTime"; - private static final String DEFAULT_MAX_AGE = "defaultMaxAge"; - private static final String TOKEN_ENDPOINT_AUTH_SIGNING_ALG = "tokenEndpointAuthSigningAlg"; - private static final String USER_INFO_ENCRYPTED_RESPONSE_ENC = "userInfoEncryptedResponseEnc"; - private static final String USER_INFO_ENCRYPTED_RESPONSE_ALG = "userInfoEncryptedResponseAlg"; - private static final String USER_INFO_SIGNED_RESPONSE_ALG = "userInfoSignedResponseAlg"; - private static final String ID_TOKEN_ENCRYPTED_RESPONSE_ENC = "idTokenEncryptedResponseEnc"; - private static final String ID_TOKEN_ENCRYPTED_RESPONSE_ALG = "idTokenEncryptedResponseAlg"; - private static final String ID_TOKEN_SIGNED_RESPONSE_ALG = "idTokenSignedResponseAlg"; - private static final String REQUEST_OBJECT_SIGNING_ALG = "requestObjectSigningAlg"; - private static final String SUBJECT_TYPE = "subjectType"; - private static final String SECTOR_IDENTIFIER_URI = "sectorIdentifierUri"; - private static final String APPLICATION_TYPE = "applicationType"; - private static final String JWKS = "jwks"; - private static final String JWKS_URI = "jwksUri"; - private static final String POLICY_URI = "policyUri"; - private static final String GRANT_TYPES = "grantTypes"; - private static final String TOKEN_ENDPOINT_AUTH_METHOD = "tokenEndpointAuthMethod"; - private static final String TOS_URI = "tosUri"; - private static final String CONTACTS = "contacts"; - private static final String LOGO_URI = "logoUri"; - private static final String REDIRECT_URIS = "redirectUris"; - private static final String REFRESH_TOKEN_VALIDITY_SECONDS = "refreshTokenValiditySeconds"; - private static final String ACCESS_TOKEN_VALIDITY_SECONDS = "accessTokenValiditySeconds"; - private static final String SECRET = "secret"; private static final String URI = "uri"; - private static final String CREATOR_USER_ID = "creatorUserId"; - private static final String APPROVED_ACCESS_TOKENS = "approvedAccessTokens"; - private static final String ALLOWED_SCOPES = "allowedScopes"; - private static final String USER_ID = "userId"; - private static final String TIMEOUT_DATE = "timeoutDate"; - private static final String CREATION_DATE = "creationDate"; - private static final String ACCESS_DATE = "accessDate"; - private static final String AUTHENTICATED = "authenticated"; - private static final String SOURCE_CLASS = "sourceClass"; private static final String NAME = "name"; - private static final String SAVED_USER_AUTHENTICATION = "savedUserAuthentication"; - private static final String EXTENSIONS = "extensions"; - private static final String RESPONSE_TYPES = "responseTypes"; - private static final String REDIRECT_URI = "redirectUri"; - private static final String APPROVED = "approved"; - private static final String AUTHORITIES = "authorities"; - private static final String RESOURCE_IDS = "resourceIds"; - private static final String REQUEST_PARAMETERS = "requestParameters"; private static final String TYPE = "type"; - private static final String SCOPE = "scope"; - private static final String ID_TOKEN_ID = "idTokenId"; - private static final String REFRESH_TOKEN_ID = "refreshTokenId"; private static final String VALUE = "value"; - private static final String AUTHENTICATION_HOLDER_ID = "authenticationHolderId"; private static final String CLIENT_ID = "clientId"; private static final String EXPIRATION = "expiration"; private static final String ID = "id"; @@ -153,7 +93,7 @@ public class UmaDataServiceExtension_1_3 extends MITREidDataServiceSupport imple private static final Logger logger = LoggerFactory.getLogger(UmaDataServiceExtension_1_3.class); - + @Autowired private SavedRegisteredClientService registeredClientService; @Autowired @@ -193,7 +133,7 @@ public void exportExtensionData(JsonWriter writer) throws IOException { writer.beginArray(); writePermissionTickets(writer); writer.endArray(); - + writer.name(TOKEN_PERMISSIONS); writer.beginArray(); writeTokenPermissions(writer); @@ -202,7 +142,7 @@ public void exportExtensionData(JsonWriter writer) throws IOException { /** * @param writer - * @throws IOException + * @throws IOException */ private void writeTokenPermissions(JsonWriter writer) throws IOException { for (OAuth2AccessTokenEntity token : tokenRepository.getAllAccessTokens()) { @@ -223,10 +163,10 @@ private void writeTokenPermissions(JsonWriter writer) throws IOException { writer.endObject(); } writer.endArray(); - + writer.endObject(); } - } + } } /** @@ -383,7 +323,7 @@ public boolean importExtensionData(String name, JsonReader reader) throws IOExce return false; } } - + /** * @param reader */ @@ -410,25 +350,25 @@ private void readTokenPermissions(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String pname = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (pname.equals(RESOURCE_SET)) { - rsid = reader.nextLong(); - } else if (pname.equals(SCOPES)) { - scope = readSet(reader); - } else { + case END_OBJECT: + continue; + case NAME: + String pname = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (pname.equals(RESOURCE_SET)) { + rsid = reader.nextLong(); + } else if (pname.equals(SCOPES)) { + scope = readSet(reader); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -450,7 +390,7 @@ private void readTokenPermissions(JsonReader reader) throws IOException { tokenToPermissionRefs.put(tokenId, permissions); } reader.endArray(); - + } private Map permissionToResourceRefs = new HashMap<>(); @@ -466,99 +406,99 @@ private void readPermissionTickets(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(CLAIMS_SUPPLIED)) { - Set claimsSupplied = new HashSet<>(); - reader.beginArray(); - while (reader.hasNext()) { - Claim c = new Claim(); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(CLAIMS_SUPPLIED)) { + Set claimsSupplied = new HashSet<>(); + reader.beginArray(); + while (reader.hasNext()) { + Claim c = new Claim(); + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String cname = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (cname.equals(ISSUER)) { + c.setIssuer(readSet(reader)); + } else if (cname.equals(CLAIM_TOKEN_FORMAT)) { + c.setClaimTokenFormat(readSet(reader)); + } else if (cname.equals(CLAIM_TYPE)) { + c.setClaimType(reader.nextString()); + } else if (cname.equals(FRIENDLY_NAME)) { + c.setFriendlyName(reader.nextString()); + } else if (cname.equals(NAME)) { + c.setName(reader.nextString()); + } else if (cname.equals(VALUE)) { + JsonElement e = parser.parse(reader.nextString()); + c.setValue(e); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } + } + reader.endObject(); + claimsSupplied.add(c); + } + reader.endArray(); + ticket.setClaimsSupplied(claimsSupplied); + } else if (name.equals(EXPIRATION)) { + ticket.setExpiration(utcToDate(reader.nextString())); + } else if (name.equals(PERMISSION)) { + Permission p = new Permission(); + Long rsid = null; reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String cname = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (cname.equals(ISSUER)) { - c.setIssuer(readSet(reader)); - } else if (cname.equals(CLAIM_TOKEN_FORMAT)) { - c.setClaimTokenFormat(readSet(reader)); - } else if (cname.equals(CLAIM_TYPE)) { - c.setClaimType(reader.nextString()); - } else if (cname.equals(FRIENDLY_NAME)) { - c.setFriendlyName(reader.nextString()); - } else if (cname.equals(NAME)) { - c.setName(reader.nextString()); - } else if (cname.equals(VALUE)) { - JsonElement e = parser.parse(reader.nextString()); - c.setValue(e); - } else { + case END_OBJECT: + continue; + case NAME: + String pname = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (pname.equals(RESOURCE_SET)) { + rsid = reader.nextLong(); + } else if (pname.equals(SCOPES)) { + p.setScopes(readSet(reader)); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); - claimsSupplied.add(c); - } - reader.endArray(); - ticket.setClaimsSupplied(claimsSupplied); - } else if (name.equals(EXPIRATION)) { - ticket.setExpiration(utcToDate(reader.nextString())); - } else if (name.equals(PERMISSION)) { - Permission p = new Permission(); - Long rsid = null; - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String pname = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (pname.equals(RESOURCE_SET)) { - rsid = reader.nextLong(); - } else if (pname.equals(SCOPES)) { - p.setScopes(readSet(reader)); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; - } + Permission saved = permissionRepository.saveRawPermission(p); + permissionToResourceRefs.put(saved.getId(), rsid); + ticket.setPermission(saved); + } else if (name.equals(TICKET)) { + ticket.setTicket(reader.nextString()); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - reader.endObject(); - Permission saved = permissionRepository.saveRawPermission(p); - permissionToResourceRefs.put(saved.getId(), rsid); - ticket.setPermission(saved); - } else if (name.equals(TICKET)) { - ticket.setTicket(reader.nextString()); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -582,114 +522,114 @@ private void readResourceSets(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ID)) { - oldId = reader.nextLong(); - } else if (name.equals(CLIENT_ID)) { - rs.setClientId(reader.nextString()); - } else if (name.equals(ICON_URI)) { - rs.setIconUri(reader.nextString()); - } else if (name.equals(NAME)) { - rs.setName(reader.nextString()); - } else if (name.equals(TYPE)) { - rs.setType(reader.nextString()); - } else if (name.equals(URI)) { - rs.setUri(reader.nextString()); - } else if (name.equals(OWNER)) { - rs.setOwner(reader.nextString()); - } else if (name.equals(POLICIES)) { - Set policies = new HashSet<>(); - reader.beginArray(); - while (reader.hasNext()) { - Policy p = new Policy(); - reader.beginObject(); + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ID)) { + oldId = reader.nextLong(); + } else if (name.equals(CLIENT_ID)) { + rs.setClientId(reader.nextString()); + } else if (name.equals(ICON_URI)) { + rs.setIconUri(reader.nextString()); + } else if (name.equals(NAME)) { + rs.setName(reader.nextString()); + } else if (name.equals(TYPE)) { + rs.setType(reader.nextString()); + } else if (name.equals(URI)) { + rs.setUri(reader.nextString()); + } else if (name.equals(OWNER)) { + rs.setOwner(reader.nextString()); + } else if (name.equals(POLICIES)) { + Set policies = new HashSet<>(); + reader.beginArray(); while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String pname = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (pname.equals(NAME)) { - p.setName(reader.nextString()); - } else if (pname.equals(SCOPES)) { - p.setScopes(readSet(reader)); - } else if (pname.equals(CLAIMS_REQUIRED)) { - Set claimsRequired = new HashSet<>(); - reader.beginArray(); - while (reader.hasNext()) { - Claim c = new Claim(); - reader.beginObject(); - while (reader.hasNext()) { - switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String cname = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (cname.equals(ISSUER)) { - c.setIssuer(readSet(reader)); - } else if (cname.equals(CLAIM_TOKEN_FORMAT)) { - c.setClaimTokenFormat(readSet(reader)); - } else if (cname.equals(CLAIM_TYPE)) { - c.setClaimType(reader.nextString()); - } else if (cname.equals(FRIENDLY_NAME)) { - c.setFriendlyName(reader.nextString()); - } else if (cname.equals(NAME)) { - c.setName(reader.nextString()); - } else if (cname.equals(VALUE)) { - JsonElement e = parser.parse(reader.nextString()); - c.setValue(e); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); + Policy p = new Policy(); + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String pname = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (pname.equals(NAME)) { + p.setName(reader.nextString()); + } else if (pname.equals(SCOPES)) { + p.setScopes(readSet(reader)); + } else if (pname.equals(CLAIMS_REQUIRED)) { + Set claimsRequired = new HashSet<>(); + reader.beginArray(); + while (reader.hasNext()) { + Claim c = new Claim(); + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.peek()) { + case END_OBJECT: + continue; + case NAME: + String cname = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (cname.equals(ISSUER)) { + c.setIssuer(readSet(reader)); + } else if (cname.equals(CLAIM_TOKEN_FORMAT)) { + c.setClaimTokenFormat(readSet(reader)); + } else if (cname.equals(CLAIM_TYPE)) { + c.setClaimType(reader.nextString()); + } else if (cname.equals(FRIENDLY_NAME)) { + c.setFriendlyName(reader.nextString()); + } else if (cname.equals(NAME)) { + c.setName(reader.nextString()); + } else if (cname.equals(VALUE)) { + JsonElement e = parser.parse(reader.nextString()); + c.setValue(e); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; + } } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + reader.endObject(); + claimsRequired.add(c); } + reader.endArray(); + p.setClaimsRequired(claimsRequired); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - reader.endObject(); - claimsRequired.add(c); - } - reader.endArray(); - p.setClaimsRequired(claimsRequired); - } else { - logger.debug("Found unexpected entry"); - reader.skipValue(); + break; + default: + logger.debug("Found unexpected entry"); + reader.skipValue(); + continue; } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; } + reader.endObject(); + policies.add(p); } - reader.endObject(); - policies.add(p); + reader.endArray(); + rs.setPolicies(policies); + } else if (name.equals(SCOPES)) { + rs.setScopes(readSet(reader)); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); } - reader.endArray(); - rs.setPolicies(policies); - } else if (name.equals(SCOPES)) { - rs.setScopes(readSet(reader)); - } else { + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -711,25 +651,25 @@ private void readSavedRegisteredClients(JsonReader reader) throws IOException{ reader.beginObject(); while (reader.hasNext()) { switch (reader.peek()) { - case END_OBJECT: - continue; - case NAME: - String name = reader.nextName(); - if (reader.peek() == JsonToken.NULL) { - reader.skipValue(); - } else if (name.equals(ISSUER)) { - issuer = reader.nextString(); - } else if (name.equals(REGISTERED_CLIENT)) { - clientString = reader.nextString(); - } else { + case END_OBJECT: + continue; + case NAME: + String name = reader.nextName(); + if (reader.peek() == JsonToken.NULL) { + reader.skipValue(); + } else if (name.equals(ISSUER)) { + issuer = reader.nextString(); + } else if (name.equals(REGISTERED_CLIENT)) { + clientString = reader.nextString(); + } else { + logger.debug("Found unexpected entry"); + reader.skipValue(); + } + break; + default: logger.debug("Found unexpected entry"); reader.skipValue(); - } - break; - default: - logger.debug("Found unexpected entry"); - reader.skipValue(); - continue; + continue; } } reader.endObject(); @@ -758,13 +698,13 @@ public void fixExtensionObjectReferences(MITREidDataServiceMaps maps) { for (Long tokenId : tokenToPermissionRefs.keySet()) { Long newTokenId = maps.getAccessTokenOldToNewIdMap().get(tokenId); OAuth2AccessTokenEntity token = tokenRepository.getAccessTokenById(newTokenId); - + Set permissions = new HashSet<>(); for (Long permissionId : tokenToPermissionRefs.get(tokenId)) { Permission p = permissionRepository.getById(permissionId); permissions.add(p); } - + token.setPermissions(permissions); tokenRepository.saveAccessToken(token); } diff --git a/uma-server/src/main/java/org/mitre/uma/util/ExternalLoginAuthoritiesMapper.java b/uma-server/src/main/java/org/mitre/uma/util/ExternalLoginAuthoritiesMapper.java index cf31d41627..496a49ad56 100644 --- a/uma-server/src/main/java/org/mitre/uma/util/ExternalLoginAuthoritiesMapper.java +++ b/uma-server/src/main/java/org/mitre/uma/util/ExternalLoginAuthoritiesMapper.java @@ -30,7 +30,7 @@ /** * Utility class to map all external logins to the ROLE_EXTERNAL_USER authority * to prevent them from accessing other parts of the server. - * + * * @author jricher * */ diff --git a/uma-server/src/main/java/org/mitre/uma/view/ResourceSetEntityAbbreviatedView.java b/uma-server/src/main/java/org/mitre/uma/view/ResourceSetEntityAbbreviatedView.java index c10eb6c88e..616ea8eac6 100644 --- a/uma-server/src/main/java/org/mitre/uma/view/ResourceSetEntityAbbreviatedView.java +++ b/uma-server/src/main/java/org/mitre/uma/view/ResourceSetEntityAbbreviatedView.java @@ -56,28 +56,28 @@ public class ResourceSetEntityAbbreviatedView extends AbstractView { private ConfigurationPropertiesBean config; private Gson gson = new GsonBuilder() - .setExclusionStrategies(new ExclusionStrategy() { - - @Override - public boolean shouldSkipField(FieldAttributes f) { - - return false; - } - - @Override - public boolean shouldSkipClass(Class clazz) { - // skip the JPA binding wrapper - if (clazz.equals(BeanPropertyBindingResult.class)) { - return true; - } - return false; - } - - }) - .serializeNulls() - .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") - .setLongSerializationPolicy(LongSerializationPolicy.STRING) - .create(); + .setExclusionStrategies(new ExclusionStrategy() { + + @Override + public boolean shouldSkipField(FieldAttributes f) { + + return false; + } + + @Override + public boolean shouldSkipClass(Class clazz) { + // skip the JPA binding wrapper + if (clazz.equals(BeanPropertyBindingResult.class)) { + return true; + } + return false; + } + + }) + .serializeNulls() + .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") + .setLongSerializationPolicy(LongSerializationPolicy.STRING) + .create(); @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) { diff --git a/uma-server/src/main/java/org/mitre/uma/view/ResourceSetEntityView.java b/uma-server/src/main/java/org/mitre/uma/view/ResourceSetEntityView.java index 2d50cdee40..bd9a558735 100644 --- a/uma-server/src/main/java/org/mitre/uma/view/ResourceSetEntityView.java +++ b/uma-server/src/main/java/org/mitre/uma/view/ResourceSetEntityView.java @@ -54,28 +54,28 @@ public class ResourceSetEntityView extends AbstractView { private ConfigurationPropertiesBean config; private Gson gson = new GsonBuilder() - .setExclusionStrategies(new ExclusionStrategy() { - - @Override - public boolean shouldSkipField(FieldAttributes f) { - - return false; - } - - @Override - public boolean shouldSkipClass(Class clazz) { - // skip the JPA binding wrapper - if (clazz.equals(BeanPropertyBindingResult.class)) { - return true; - } - return false; - } - - }) - .serializeNulls() - .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") - .setLongSerializationPolicy(LongSerializationPolicy.STRING) - .create(); + .setExclusionStrategies(new ExclusionStrategy() { + + @Override + public boolean shouldSkipField(FieldAttributes f) { + + return false; + } + + @Override + public boolean shouldSkipClass(Class clazz) { + // skip the JPA binding wrapper + if (clazz.equals(BeanPropertyBindingResult.class)) { + return true; + } + return false; + } + + }) + .serializeNulls() + .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") + .setLongSerializationPolicy(LongSerializationPolicy.STRING) + .create(); @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) { diff --git a/uma-server/src/main/java/org/mitre/uma/web/AuthorizationRequestEndpoint.java b/uma-server/src/main/java/org/mitre/uma/web/AuthorizationRequestEndpoint.java index 67af05c070..79fe62b8eb 100644 --- a/uma-server/src/main/java/org/mitre/uma/web/AuthorizationRequestEndpoint.java +++ b/uma-server/src/main/java/org/mitre/uma/web/AuthorizationRequestEndpoint.java @@ -23,7 +23,6 @@ import org.mitre.oauth2.service.OAuth2TokenEntityService; import org.mitre.oauth2.service.SystemScopeService; import org.mitre.oauth2.web.AuthenticationUtilities; -import org.mitre.openid.connect.service.OIDCTokenService; import org.mitre.openid.connect.view.HttpCodeView; import org.mitre.openid.connect.view.JsonEntityView; import org.mitre.openid.connect.view.JsonErrorView; @@ -74,9 +73,6 @@ public class AuthorizationRequestEndpoint { @Autowired private OAuth2TokenEntityService tokenService; - @Autowired - private OIDCTokenService oidcTokenService; - @Autowired private ClaimsProcessingService claimsProcessingService; diff --git a/uma-server/src/main/java/org/mitre/uma/web/ClaimsCollectionEndpoint.java b/uma-server/src/main/java/org/mitre/uma/web/ClaimsCollectionEndpoint.java index d281a51d2b..7cc8c1e8f6 100644 --- a/uma-server/src/main/java/org/mitre/uma/web/ClaimsCollectionEndpoint.java +++ b/uma-server/src/main/java/org/mitre/uma/web/ClaimsCollectionEndpoint.java @@ -46,9 +46,9 @@ import com.google.gson.JsonPrimitive; /** - * + * * Collect claims interactively from the end user. - * + * * @author jricher * */ diff --git a/uma-server/src/main/java/org/mitre/uma/web/PermissionRegistrationEndpoint.java b/uma-server/src/main/java/org/mitre/uma/web/PermissionRegistrationEndpoint.java index e4374f0b7a..f048656aff 100644 --- a/uma-server/src/main/java/org/mitre/uma/web/PermissionRegistrationEndpoint.java +++ b/uma-server/src/main/java/org/mitre/uma/web/PermissionRegistrationEndpoint.java @@ -17,6 +17,10 @@ package org.mitre.uma.web; +import static org.mitre.oauth2.web.AuthenticationUtilities.ensureOAuthScope; +import static org.mitre.util.JsonUtils.getAsLong; +import static org.mitre.util.JsonUtils.getAsStringSet; + import java.util.Set; import org.mitre.oauth2.model.SystemScope; @@ -45,10 +49,6 @@ import com.google.gson.JsonParseException; import com.google.gson.JsonParser; -import static org.mitre.oauth2.web.AuthenticationUtilities.ensureOAuthScope; -import static org.mitre.util.JsonUtils.getAsLong; -import static org.mitre.util.JsonUtils.getAsStringSet; - /** * @author jricher * diff --git a/uma-server/src/main/java/org/mitre/uma/web/PolicyAPI.java b/uma-server/src/main/java/org/mitre/uma/web/PolicyAPI.java index a33b133928..6b25b02df8 100644 --- a/uma-server/src/main/java/org/mitre/uma/web/PolicyAPI.java +++ b/uma-server/src/main/java/org/mitre/uma/web/PolicyAPI.java @@ -48,7 +48,7 @@ /** * API for managing policies on resource sets. - * + * * @author jricher * */ diff --git a/uma-server/src/main/java/org/mitre/uma/web/ResourceSetRegistrationEndpoint.java b/uma-server/src/main/java/org/mitre/uma/web/ResourceSetRegistrationEndpoint.java index 6e1d49bb98..5bbaa7705d 100644 --- a/uma-server/src/main/java/org/mitre/uma/web/ResourceSetRegistrationEndpoint.java +++ b/uma-server/src/main/java/org/mitre/uma/web/ResourceSetRegistrationEndpoint.java @@ -17,6 +17,11 @@ package org.mitre.uma.web; +import static org.mitre.oauth2.web.AuthenticationUtilities.ensureOAuthScope; +import static org.mitre.util.JsonUtils.getAsLong; +import static org.mitre.util.JsonUtils.getAsString; +import static org.mitre.util.JsonUtils.getAsStringSet; + import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -53,11 +58,6 @@ import com.google.gson.JsonParseException; import com.google.gson.JsonParser; -import static org.mitre.oauth2.web.AuthenticationUtilities.ensureOAuthScope; -import static org.mitre.util.JsonUtils.getAsLong; -import static org.mitre.util.JsonUtils.getAsString; -import static org.mitre.util.JsonUtils.getAsStringSet; - @Controller @RequestMapping("/" + ResourceSetRegistrationEndpoint.URL) @PreAuthorize("hasRole('ROLE_USER')") @@ -298,9 +298,9 @@ private ResourceSet parseResourceSet(String jsonString) { /** - * + * * Make sure the resource set doesn't have any restricted or reserved scopes. - * + * * @param rs */ private ResourceSet validateScopes(ResourceSet rs) { diff --git a/uma-server/src/test/java/org/mitre/uma/service/impl/TestDefaultPermissionService.java b/uma-server/src/test/java/org/mitre/uma/service/impl/TestDefaultPermissionService.java index 90763ae611..34c448c63f 100644 --- a/uma-server/src/test/java/org/mitre/uma/service/impl/TestDefaultPermissionService.java +++ b/uma-server/src/test/java/org/mitre/uma/service/impl/TestDefaultPermissionService.java @@ -17,6 +17,8 @@ package org.mitre.uma.service.impl; +import static org.mockito.Matchers.anySetOf; + import java.util.Set; import java.util.UUID; @@ -41,8 +43,6 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.not; -import static org.mockito.Matchers.anySetOf; - import static org.mockito.Mockito.when; import static org.junit.Assert.assertNotNull; diff --git a/uma-server/src/test/java/org/mitre/uma/service/impl/TestDefaultResourceSetService.java b/uma-server/src/test/java/org/mitre/uma/service/impl/TestDefaultResourceSetService.java index a8de434c17..f8865129ec 100644 --- a/uma-server/src/test/java/org/mitre/uma/service/impl/TestDefaultResourceSetService.java +++ b/uma-server/src/test/java/org/mitre/uma/service/impl/TestDefaultResourceSetService.java @@ -17,6 +17,8 @@ package org.mitre.uma.service.impl; +import static org.mockito.Matchers.any; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -27,8 +29,6 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import static org.mockito.Matchers.any; - import static org.mockito.Mockito.when; /** From bd72b4138d974c383efb1460087d15832047412e Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 21 Mar 2017 14:11:40 -0400 Subject: [PATCH 172/269] added missing copyright declarations --- .../client/AuthorizationEndpointException.java | 16 ++++++++++++++++ .../client/TestOIDCAuthenticationFilter.java | 16 ++++++++++++++++ .../data/AbstractPageOperationTemplate.java | 16 ++++++++++++++++ .../org/mitre/data/DefaultPageCriteria.java | 16 ++++++++++++++++ .../main/java/org/mitre/data/PageCriteria.java | 16 ++++++++++++++++ .../data/AbstractPageOperationTemplateTest.java | 16 ++++++++++++++++ .../db/oracle/entity-mappings_oracle.xml | 16 ++++++++++++++++ .../src/main/webapp/WEB-INF/locale-config.xml | 17 +++++++++++++++++ .../src/main/webapp/WEB-INF/wro.properties | 16 ++++++++++++++++ .../src/main/webapp/WEB-INF/wro.xml | 16 ++++++++++++++++ .../src/main/webapp/resources/js/profile.js | 16 ++++++++++++++++ 11 files changed, 177 insertions(+) diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java index 23b5773f5d..9e0226227f 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java @@ -1,3 +1,19 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.openid.connect.client; import org.springframework.security.authentication.AuthenticationServiceException; diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/TestOIDCAuthenticationFilter.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/TestOIDCAuthenticationFilter.java index 46ae7397f1..11563a1a81 100644 --- a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/TestOIDCAuthenticationFilter.java +++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/TestOIDCAuthenticationFilter.java @@ -1,3 +1,19 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.openid.connect.client; import javax.servlet.http.HttpServletRequest; diff --git a/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java b/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java index 7911f2f029..fb7672bc5b 100644 --- a/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java +++ b/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java @@ -1,3 +1,19 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.data; import java.util.Collection; diff --git a/openid-connect-common/src/main/java/org/mitre/data/DefaultPageCriteria.java b/openid-connect-common/src/main/java/org/mitre/data/DefaultPageCriteria.java index 5b0f659bb2..911a92f5f3 100644 --- a/openid-connect-common/src/main/java/org/mitre/data/DefaultPageCriteria.java +++ b/openid-connect-common/src/main/java/org/mitre/data/DefaultPageCriteria.java @@ -1,3 +1,19 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.data; /** diff --git a/openid-connect-common/src/main/java/org/mitre/data/PageCriteria.java b/openid-connect-common/src/main/java/org/mitre/data/PageCriteria.java index 0c462434ab..5f822440ab 100644 --- a/openid-connect-common/src/main/java/org/mitre/data/PageCriteria.java +++ b/openid-connect-common/src/main/java/org/mitre/data/PageCriteria.java @@ -1,3 +1,19 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.data; /** diff --git a/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java b/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java index 9bc11020fd..1859b9a82f 100644 --- a/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java +++ b/openid-connect-common/src/test/java/org/mitre/data/AbstractPageOperationTemplateTest.java @@ -1,3 +1,19 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.data; import java.util.ArrayList; diff --git a/openid-connect-server-webapp/src/main/resources/db/oracle/entity-mappings_oracle.xml b/openid-connect-server-webapp/src/main/resources/db/oracle/entity-mappings_oracle.xml index b1b074ac8b..4920918fb7 100644 --- a/openid-connect-server-webapp/src/main/resources/db/oracle/entity-mappings_oracle.xml +++ b/openid-connect-server-webapp/src/main/resources/db/oracle/entity-mappings_oracle.xml @@ -1,3 +1,19 @@ + + + diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.properties b/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.properties index 8b8c9dd890..3940bd137c 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.properties +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.properties @@ -1,2 +1,18 @@ +############################################################################### +# Copyright 2017 The MITRE Corporation +# and the MIT Internet Trust Consortium +# +# 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. +############################################################################### preProcessors=cssImport postProcessors=lessCss diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.xml index fdd66a0b40..3d5a6c17e0 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.xml @@ -1,3 +1,19 @@ + /less/bootstrap.less diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/profile.js b/openid-connect-server-webapp/src/main/webapp/resources/js/profile.js index 4945ac711d..e9fd39ffe2 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/profile.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/profile.js @@ -1,3 +1,19 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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. + *******************************************************************************/ ui.routes.push({path: "user/profile", name: "profile", callback: function() { From 78b9b6ced4b64208b314f16e86567520ff0f1cdc Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 21 Mar 2017 15:04:18 -0400 Subject: [PATCH 173/269] auto format and cleanup javascript --- .../src/main/webapp/resources/js/admin.js | 818 ++--- .../src/main/webapp/resources/js/blacklist.js | 274 +- .../src/main/webapp/resources/js/client.js | 2618 +++++++++-------- .../src/main/webapp/resources/js/dynreg.js | 1363 +++++---- .../src/main/webapp/resources/js/grant.js | 313 +- .../src/main/webapp/resources/js/profile.js | 39 +- .../src/main/webapp/resources/js/rsreg.js | 929 +++--- .../src/main/webapp/resources/js/scope.js | 643 ++-- .../src/main/webapp/resources/js/token.js | 738 ++--- .../src/main/webapp/resources/js/whitelist.js | 759 ++--- 10 files changed, 4554 insertions(+), 3940 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js b/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js index fdbacdfbb0..18c2efeec1 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/admin.js @@ -53,344 +53,386 @@ Backbone.Collection.prototype.fetchIfNeeded = function(options) { } }; -var URIModel = Backbone.Model.extend({ +var URIModel = Backbone.Model + .extend({ - validate: function(attrs){ + validate: function(attrs) { - var expression = /^(?:([a-z0-9+.-]+:\/\/)((?:(?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*)@)?((?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*)(:(?:\d*))?(\/(?:[a-z0-9-._~!$&'()*+,;=:@\/]|%[0-9A-F]{2})*)?|([a-z0-9+.-]+:)(\/?(?:[a-z0-9-._~!$&'()*+,;=:@]|%[0-9A-F]{2})+(?:[a-z0-9-._~!$&'()*+,;=:@\/]|%[0-9A-F]{2})*)?)(\?(?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*)?(#(?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*)?$/i; - var regex = new RegExp(expression); + var expression = /^(?:([a-z0-9+.-]+:\/\/)((?:(?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*)@)?((?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*)(:(?:\d*))?(\/(?:[a-z0-9-._~!$&'()*+,;=:@\/]|%[0-9A-F]{2})*)?|([a-z0-9+.-]+:)(\/?(?:[a-z0-9-._~!$&'()*+,;=:@]|%[0-9A-F]{2})+(?:[a-z0-9-._~!$&'()*+,;=:@\/]|%[0-9A-F]{2})*)?)(\?(?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*)?(#(?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*)?$/i; + var regex = new RegExp(expression); - if (attrs.item == null || !attrs.item.match(regex)) { - return "Invalid URI"; - } - } - -}); + if (attrs.item == null || !attrs.item.match(regex)) { + return "Invalid URI"; + } + } + }); /* -* Backbone JS Reusable ListWidget -* Options -* { -* collection: Backbone JS Collection -* type: ('uri'|'default') -* autocomplete: ['item1','item2'] List of auto complete items -* } -* + * Backbone JS Reusable ListWidget Options { collection: Backbone JS Collection + * type: ('uri'|'default') autocomplete: ['item1','item2'] List of auto complete + * items } + * */ var ListWidgetChildView = Backbone.View.extend({ - tagName: 'tr', - - events:{ - "click .btn-delete-list-item":'deleteItem', - "change .checkbox-list-item":'toggleCheckbox' - }, - - deleteItem:function (e) { - e.preventDefault(); - e.stopImmediatePropagation(); - //this.$el.tooltip('delete'); - - this.model.destroy({ - dataType: false, processData: false, - error:app.errorHandlerView.handleError() - }); - - }, - - toggleCheckbox:function(e) { - e.preventDefault(); - e.stopImmediatePropagation(); - if ($(e.target).is(':checked')) { - this.options.collection.add(this.model); - } else { - this.options.collection.remove(this.model); - } - - }, - - initialize:function (options) { - this.options = {toggle: false, checked: false}; - _.extend(this.options, options); - if (!this.template) { - this.template = _.template($('#tmpl-list-widget-child').html()); - } - }, - - render:function () { - - var data = {model: this.model.toJSON(), opt: this.options}; - - this.$el.html(this.template(data)); - - $('.item-full', this.el).hide(); - - if (this.model.get('item').length > 30) { - this.$el.tooltip({title:$.t('admin.list-widget.tooltip')}); - - var _self = this; - - $(this.el).click(function(event) { - event.preventDefault(); - $('.item-short', _self.el).hide(); - $('.item-full', _self.el).show(); - _self.$el.tooltip('destroy'); - }); - } - - - - $(this.el).i18n(); - return this; - } + tagName: 'tr', + + events: { + "click .btn-delete-list-item": 'deleteItem', + "change .checkbox-list-item": 'toggleCheckbox' + }, + + deleteItem: function(e) { + e.preventDefault(); + e.stopImmediatePropagation(); + // this.$el.tooltip('delete'); + + this.model.destroy({ + dataType: false, + processData: false, + error: app.errorHandlerView.handleError() + }); + + }, + + toggleCheckbox: function(e) { + e.preventDefault(); + e.stopImmediatePropagation(); + if ($(e.target).is(':checked')) { + this.options.collection.add(this.model); + } else { + this.options.collection.remove(this.model); + } + + }, + + initialize: function(options) { + this.options = { + toggle: false, + checked: false + }; + _.extend(this.options, options); + if (!this.template) { + this.template = _.template($('#tmpl-list-widget-child').html()); + } + }, + + render: function() { + + var data = { + model: this.model.toJSON(), + opt: this.options + }; + + this.$el.html(this.template(data)); + + $('.item-full', this.el).hide(); + + if (this.model.get('item').length > 30) { + this.$el.tooltip({ + title: $.t('admin.list-widget.tooltip') + }); + + var _self = this; + + $(this.el).click(function(event) { + event.preventDefault(); + $('.item-short', _self.el).hide(); + $('.item-full', _self.el).show(); + _self.$el.tooltip('destroy'); + }); + } + + $(this.el).i18n(); + return this; + } }); var ListWidgetView = Backbone.View.extend({ - tagName: "div", - - events:{ - "click .btn-add-list-item":"addItem", - "keypress":function (e) { - // trap the enter key - if (e.which == 13) { - e.preventDefault(); - this.addItem(e); - $("input", this.$el).focus(); - } - } - }, - - initialize:function (options) { - this.options = options; - - if (!this.template) { - this.template = _.template($('#tmpl-list-widget').html()); - } - - this.collection.bind('add', this.render, this); - this.collection.bind('remove', this.render, this); - }, - - addItem:function(e) { - e.preventDefault(); - - var input_value = $("input", this.el).val().trim(); - - if (input_value === ""){ - return; - } - - var model; - - if (this.options.type == 'uri') { - model = new URIModel({item:input_value}); - } else { - model = new Backbone.Model({item:input_value}); - model.validate = function(attrs) { - if(!attrs.item) { - return "value can't be null"; - } - }; - } - - // if it's valid and doesn't already exist - if (model.get("item") != null && this.collection.where({item: input_value}).length < 1) { - this.collection.add(model); - } else { - // else add a visual error indicator - $(".control-group", this.el).addClass('error'); - } - }, - - render:function (eventName) { - - this.$el.html(this.template({placeholder:this.options.placeholder, - helpBlockText:this.options.helpBlockText})); - - var _self = this; - - if (_.size(this.collection.models) == 0 && _.size(this.options.autocomplete) == 0) { - $("tbody", _self.el).html($('#tmpl-list-widget-child-empty').html()); - } else { - - // make a copy of our collection to work from - var values = this.collection.clone(); - - // look through our autocomplete values (if we have them) and render them all as checkboxes - if (this.options.autocomplete) { - _.each(this.options.autocomplete, function(option) { - var found = _.find(values.models, function(element) { - return element.get('item') == option; - }); - - var model = null; - var checked = false; - - if (found) { - // if we found the element, check the box - model = found; - checked = true; - // and remove it from the list of items to be rendered later - values.remove(found, {silent: true}); - } else { - model = new Backbone.Model({item:option}); - checked = false; - } - - var el = new ListWidgetChildView({model:model, toggle: true, checked: checked, collection: _self.collection}).render().el; - $("tbody", _self.el).append(el); - - }, this); - } - - - // now render everything not in the autocomplete list - _.each(values.models, function (model) { - var el = new ListWidgetChildView({model:model, collection: _self.collection}).render().el; - $("tbody", _self.el).append(el); - }, this); - } - - $(this.el).i18n(); - return this; - } - -}); + tagName: "div", -var BreadCrumbView = Backbone.View.extend({ + events: { + "click .btn-add-list-item": "addItem", + "keypress": function(e) { + // trap the enter key + if (e.which == 13) { + e.preventDefault(); + this.addItem(e); + $("input", this.$el).focus(); + } + } + }, + + initialize: function(options) { + this.options = options; + + if (!this.template) { + this.template = _.template($('#tmpl-list-widget').html()); + } + + this.collection.bind('add', this.render, this); + this.collection.bind('remove', this.render, this); + }, + + addItem: function(e) { + e.preventDefault(); - tagName: 'ul', + var input_value = $("input", this.el).val().trim(); - initialize:function (options) { - this.options = options; + if (input_value === "") { + return; + } - if (!this.template) { - this.template = _.template($('#tmpl-breadcrumbs').html()); - } + var model; + + if (this.options.type == 'uri') { + model = new URIModel({ + item: input_value + }); + } else { + model = new Backbone.Model({ + item: input_value + }); + model.validate = function(attrs) { + if (!attrs.item) { + return "value can't be null"; + } + }; + } - this.$el.addClass('breadcrumb'); + // if it's valid and doesn't already exist + if (model.get("item") != null && this.collection.where({ + item: input_value + }).length < 1) { + this.collection.add(model); + } else { + // else add a visual error indicator + $(".control-group", this.el).addClass('error'); + } + }, - this.collection.bind('add', this.render, this); - }, + render: function(eventName) { - render:function () { + this.$el.html(this.template({ + placeholder: this.options.placeholder, + helpBlockText: this.options.helpBlockText + })); - this.$el.empty(); - var parent = this; + var _self = this; - // go through each of the breadcrumb models - _.each(this.collection.models, function (crumb, index) { + if (_.size(this.collection.models) == 0 && _.size(this.options.autocomplete) == 0) { + $("tbody", _self.el).html($('#tmpl-list-widget-child-empty').html()); + } else { + + // make a copy of our collection to work from + var values = this.collection.clone(); + + // look through our autocomplete values (if we have them) and render + // them all as checkboxes + if (this.options.autocomplete) { + _.each(this.options.autocomplete, function(option) { + var found = _.find(values.models, function(element) { + return element.get('item') == option; + }); + + var model = null; + var checked = false; + + if (found) { + // if we found the element, check the box + model = found; + checked = true; + // and remove it from the list of items to be rendered + // later + values.remove(found, { + silent: true + }); + } else { + model = new Backbone.Model({ + item: option + }); + checked = false; + } + + var el = new ListWidgetChildView({ + model: model, + toggle: true, + checked: checked, + collection: _self.collection + }).render().el; + $("tbody", _self.el).append(el); + + }, this); + } - // if it's the last index in the crumbs then render the link inactive - if (index == parent.collection.size() - 1) { - crumb.set({active:true}, {silent:true}); - } else { - crumb.set({active:false}, {silent:true}); - } + // now render everything not in the autocomplete list + _.each(values.models, function(model) { + var el = new ListWidgetChildView({ + model: model, + collection: _self.collection + }).render().el; + $("tbody", _self.el).append(el); + }, this); + } - this.$el.append(this.template(crumb.toJSON())); - }, this); + $(this.el).i18n(); + return this; + } - $('#breadcrumbs').html(this.el); - $(this.el).i18n(); - } }); +var BreadCrumbView = Backbone.View.extend({ + + tagName: 'ul', + + initialize: function(options) { + this.options = options; + + if (!this.template) { + this.template = _.template($('#tmpl-breadcrumbs').html()); + } + + this.$el.addClass('breadcrumb'); + + this.collection.bind('add', this.render, this); + }, + + render: function() { + + this.$el.empty(); + var parent = this; + + // go through each of the breadcrumb models + _.each(this.collection.models, function(crumb, index) { + + // if it's the last index in the crumbs then render the link + // inactive + if (index == parent.collection.size() - 1) { + crumb.set({ + active: true + }, { + silent: true + }); + } else { + crumb.set({ + active: false + }, { + silent: true + }); + } + + this.$el.append(this.template(crumb.toJSON())); + }, this); + + $('#breadcrumbs').html(this.el); + $(this.el).i18n(); + } +}); // User Profile var UserProfileView = Backbone.View.extend({ tagName: 'span', - - initialize:function(options) { - this.options = options; - if (!this.template) { - this.template = _.template($('#tmpl-user-profile-element').html()); - } + + initialize: function(options) { + this.options = options; + if (!this.template) { + this.template = _.template($('#tmpl-user-profile-element').html()); + } }, - - render:function() { - - $(this.el).html($('#tmpl-user-profile').html()); - - var t = this.template; - - _.each(this.model, function (value, key) { - if (key && value) { - - if (typeof(value) === 'object') { - - var el = this.el; - var k = key; - - _.each(value, function (value, key) { - $('dl', el).append( - t({key: key, value: value, category: k}) - ); - }); - } else if (typeof(value) === 'array') { - // TODO: handle array types - } else { - $('dl', this.el).append( - t({key: key, value: value}) - ); - } - } - }, this); - - $(this.el).i18n(); + + render: function() { + + $(this.el).html($('#tmpl-user-profile').html()); + + var t = this.template; + + _.each(this.model, function(value, key) { + if (key && value) { + + if (typeof (value) === 'object') { + + var el = this.el; + var k = key; + + _.each(value, function(value, key) { + $('dl', el).append(t({ + key: key, + value: value, + category: k + })); + }); + } else if (typeof (value) === 'array') { + // TODO: handle array types + } else { + $('dl', this.el).append(t({ + key: key, + value: value + })); + } + } + }, this); + + $(this.el).i18n(); return this; } }); // error handler var ErrorHandlerView = Backbone.View.extend({ - - initialize:function(options) { - this.options = options; - if (!this.template) { - this.template = _.template($('#tmpl-error-box').html()); - } - if (!this.headerTemplate) { - this.headerTemplate = _.template($('#tmpl-error-header').html()); - } + + initialize: function(options) { + this.options = options; + if (!this.template) { + this.template = _.template($('#tmpl-error-box').html()); + } + if (!this.headerTemplate) { + this.headerTemplate = _.template($('#tmpl-error-header').html()); + } }, - - reloadPage:function(event) { + + reloadPage: function(event) { event.preventDefault(); window.location.reload(true); }, - handleError:function(message) { - + handleError: function(message) { + if (!message) { message = {}; } var _self = this; - + return function(model, response, options) { - + if (message.log) { console.log(message.log); } - - _self.showErrorMessage( - _self.headerTemplate({message: message, model: model, response: response, options: options}), - _self.template({message: message, model: model, response: response, options: options}) - ); - + + _self.showErrorMessage(_self.headerTemplate({ + message: message, + model: model, + response: response, + options: options + }), _self.template({ + message: message, + model: model, + response: response, + options: options + })); + $('#modalAlert .modal-body .page-reload').on('click', _self.reloadPage); } - }, - - showErrorMessage:function(header, message) { + }, + + showErrorMessage: function(header, message) { // hide the sheet if it's visible $('#loadingbox').sheet('hide'); - + $('#modalAlert').i18n(); $('#modalAlert div.modal-header').html(header); $('#modalAlert .modal-body').html(message); @@ -400,62 +442,66 @@ var ErrorHandlerView = Backbone.View.extend({ 'keyboard': true, 'show': true }); - + } }); - // Router var AppRouter = Backbone.Router.extend({ - routes:{ - - "": "root" - - }, - - root:function() { - if (isAdmin()) { - this.navigate('admin/clients', {trigger: true}); - } else { - this.navigate('user/approved', {trigger: true}); - } - }, - - initialize:function () { - - this.breadCrumbView = new BreadCrumbView({ - collection:new Backbone.Collection() - }); - - this.breadCrumbView.render(); - - this.errorHandlerView = new ErrorHandlerView(); - - // call all the extra initialization functions - var app = this; - _.each(ui.init, function(fn) { - fn(app); - }); - - }, - - notImplemented:function(){ - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""} - ]); - - this.updateSidebar('none'); - - $('#content').html("

    Not implemented yet.

    "); - }, - - updateSidebar:function(item) { - $('.sidebar-nav li.active').removeClass('active'); - - $('.sidebar-nav li a[href^="manage/#' + item + '"]').parent().addClass('active'); - } + routes: { + + "": "root" + + }, + + root: function() { + if (isAdmin()) { + this.navigate('admin/clients', { + trigger: true + }); + } else { + this.navigate('user/approved', { + trigger: true + }); + } + }, + + initialize: function() { + + this.breadCrumbView = new BreadCrumbView({ + collection: new Backbone.Collection() + }); + + this.breadCrumbView.render(); + + this.errorHandlerView = new ErrorHandlerView(); + + // call all the extra initialization functions + var app = this; + _.each(ui.init, function(fn) { + fn(app); + }); + + }, + + notImplemented: function() { + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }]); + + this.updateSidebar('none'); + + $('#content').html("

    Not implemented yet.

    "); + }, + + updateSidebar: function(item) { + $('.sidebar-nav li.active').removeClass('active'); + + $('.sidebar-nav li a[href^="manage/#' + item + '"]').parent().addClass('active'); + } }); // holds the global app. @@ -463,59 +509,67 @@ var AppRouter = Backbone.Router.extend({ var app = null; // main -$(function () { - - var loader = function(source) { - return $.get(source, function (templates) { - console.log('Loading file: ' + source); - $('#templates').append(templates); - }); - }; - - // load templates and append them to the body - $.when.apply(null, ui.templates.map(loader) - ).done(function() { - console.log('done'); - $.ajaxSetup({cache:false}); - app = new AppRouter(); - - _.each(ui.routes.reverse(), function(route) { - console.log("Adding route: " + route.name); - app.route(route.path, route.name, route.callback); - }); - - app.on('route', function(name, args) { - // scroll to top of page on new route selection - $("html, body").animate({ scrollTop: 0 }, "slow"); - }); - - // grab all hashed URLs and send them through the app router instead - $(document).on('click', 'a[href^="manage/#"]', function(event) { - event.preventDefault(); - app.navigate(this.hash.slice(1), {trigger: true}); - }); - - var base = $('base').attr('href'); - $.getJSON(base + '.well-known/openid-configuration', function(data) { - app.serverConfiguration = data; - var baseUrl = $.url(app.serverConfiguration.issuer); - Backbone.history.start({pushState: true, root: baseUrl.attr('relative') + 'manage/'}); - }); - }); - - window.onerror = function ( message, filename, lineno, colno, error ){ - console.log(message); - //Display an alert with an error message +$(function() { + + var loader = function(source) { + return $.get(source, function(templates) { + console.log('Loading file: ' + source); + $('#templates').append(templates); + }); + }; + + // load templates and append them to the body + $.when.apply(null, ui.templates.map(loader)).done(function() { + console.log('done'); + $.ajaxSetup({ + cache: false + }); + app = new AppRouter(); + + _.each(ui.routes.reverse(), function(route) { + console.log("Adding route: " + route.name); + app.route(route.path, route.name, route.callback); + }); + + app.on('route', function(name, args) { + // scroll to top of page on new route selection + $("html, body").animate({ + scrollTop: 0 + }, "slow"); + }); + + // grab all hashed URLs and send them through the app router instead + $(document).on('click', 'a[href^="manage/#"]', function(event) { + event.preventDefault(); + app.navigate(this.hash.slice(1), { + trigger: true + }); + }); + + var base = $('base').attr('href'); + $.getJSON(base + '.well-known/openid-configuration', function(data) { + app.serverConfiguration = data; + var baseUrl = $.url(app.serverConfiguration.issuer); + Backbone.history.start({ + pushState: true, + root: baseUrl.attr('relative') + 'manage/' + }); + }); + }); + + window.onerror = function(message, filename, lineno, colno, error) { + console.log(message); + // Display an alert with an error message $('#modalAlert div.modal-header').html($.t('error.title')); $('#modalAlert div.modal-body').html($.t('error.message') + message + '
    ' + [filename, lineno, colno, error]); - - $("#modalAlert").modal({ // wire up the actual modal functionality and show the dialog - "backdrop" : "static", - "keyboard" : true, - "show" : true // ensure the modal is shown immediately - }); - - } -}); + $("#modalAlert").modal({ // wire up the actual modal functionality + // and show the dialog + "backdrop": "static", + "keyboard": true, + "show": true + // ensure the modal is shown immediately + }); + } +}); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js b/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js index 719a685172..7b14046990 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/blacklist.js @@ -16,63 +16,64 @@ *******************************************************************************/ var BlackListModel = Backbone.Model.extend({ idAttribute: 'id', - + urlRoot: 'api/blacklist' }); var BlackListCollection = Backbone.Collection.extend({ - initialize: function() { }, + initialize: function() { + }, url: "api/blacklist" }); var BlackListListView = Backbone.View.extend({ tagName: 'span', - - initialize:function(options) { - this.options = options; + + initialize: function(options) { + this.options = options; + }, + + load: function(callback) { + if (this.collection.isFetched) { + callback(); + return; + } + + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t('admin.blacklist') + ' '); + + $.when(this.collection.fetchIfNeeded({ + success: function(e) { + $('#loading-blacklist').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + callback(); + }); }, - load:function(callback) { - if (this.collection.isFetched) { - callback(); - return; - } - - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t('admin.blacklist') + ' ' - ); - - $.when(this.collection.fetchIfNeeded({success:function(e) {$('#loading-blacklist').addClass('label-success');}, error:app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - callback(); - }); - }, - events: { - "click .refresh-table":"refreshTable", - "click .btn-add":"addItem", - "submit #add-blacklist form":"addItem" + "click .refresh-table": "refreshTable", + "click .btn-add": "addItem", + "submit #add-blacklist form": "addItem" }, - refreshTable:function(e) { - e.preventDefault(); - - var _self = this; - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t('admin.blacklist') + ' ' - ); - - $.when(this.collection.fetch()).done(function() { - $('#loadingbox').sheet('hide'); - _self.render(); - }); - }, - - togglePlaceholder:function() { + refreshTable: function(e) { + e.preventDefault(); + + var _self = this; + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t('admin.blacklist') + ' '); + + $.when(this.collection.fetch()).done(function() { + $('#loadingbox').sheet('hide'); + _self.render(); + }); + }, + + togglePlaceholder: function() { if (this.collection.length > 0) { $('#blacklist-table', this.el).show(); $('#blacklist-table-empty', this.el).hide(); @@ -81,136 +82,143 @@ var BlackListListView = Backbone.View.extend({ $('#blacklist-table-empty', this.el).show(); } }, - - render:function (eventName) { - + + render: function(eventName) { + $(this.el).html($('#tmpl-blacklist-table').html()); - + var _self = this; _.each(this.collection.models, function(blacklist) { - var view = new BlackListWidgetView({model: blacklist}); + var view = new BlackListWidgetView({ + model: blacklist + }); view.parentView = _self; $("#blacklist-table", _self.el).append(view.render().el); }, this); - + this.togglePlaceholder(); - - $(this.el).i18n(); + + $(this.el).i18n(); return this; }, - - addItem:function(e) { - e.preventDefault(); - - var input_value = $("#blacklist-uri", this.el).val().trim(); - - if (input_value === "") { - return; - } - - // TODO: URI/pattern validation, check against existing clients - - var item = new BlackListModel({ - uri: input_value - }); - - var _self = this; // closures... - - item.save({}, { - success:function() { - _self.collection.add(item); - _self.render(); - }, - error:app.errorHandlerView.handleError() - }); + + addItem: function(e) { + e.preventDefault(); + + var input_value = $("#blacklist-uri", this.el).val().trim(); + + if (input_value === "") { + return; + } + + // TODO: URI/pattern validation, check against existing clients + + var item = new BlackListModel({ + uri: input_value + }); + + var _self = this; // closures... + + item.save({}, { + success: function() { + _self.collection.add(item); + _self.render(); + }, + error: app.errorHandlerView.handleError() + }); } }); var BlackListWidgetView = Backbone.View.extend({ - + tagName: 'tr', - - initialize:function(options) { + + initialize: function(options) { this.options = options; - + if (!this.template) { this.template = _.template($('#tmpl-blacklist-item').html()); } }, - - render:function() { + + render: function() { this.$el.html(this.template(this.model.toJSON())); - return this; - + return this; + }, - - events:{ - 'click .btn-delete':'deleteBlacklist' + + events: { + 'click .btn-delete': 'deleteBlacklist' }, - - deleteBlacklist:function (e) { - e.preventDefault(); - - if (confirm($.t("blacklist.confirm"))) { - var _self = this; - - this.model.destroy({ - dataType: false, processData: false, - success:function () { - - _self.$el.fadeTo("fast", 0.00, function () { //fade - $(this).slideUp("fast", function () { //slide up - $(this).remove(); //then remove from the DOM - _self.parentView.togglePlaceholder(); - }); - }); - }, - error:app.errorHandlerView.handleError() - }); - - _self.parentView.delegateEvents(); - } - - return false; - } + + deleteBlacklist: function(e) { + e.preventDefault(); + + if (confirm($.t("blacklist.confirm"))) { + var _self = this; + + this.model.destroy({ + dataType: false, + processData: false, + success: function() { + + _self.$el.fadeTo("fast", 0.00, function() { // fade + $(this).slideUp("fast", function() { // slide up + $(this).remove(); // then remove from the DOM + _self.parentView.togglePlaceholder(); + }); + }); + }, + error: app.errorHandlerView.handleError() + }); + + _self.parentView.delegateEvents(); + } + + return false; + } }); +ui.routes.push({ + path: "admin/blacklist", + name: "blackList", + callback: function() { -ui.routes.push({path: "admin/blacklist", name: "blackList", callback: - function() { - if (!isAdmin()) { this.root(); return; } - + this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.manage-blacklist'), href:"manage/#admin/blacklist"} - ]); - - this.updateSidebar('admin/blacklist'); - - var view = new BlackListListView({collection: this.blackListList}); - - view.load( - function(collection, response, options) { - $('#content').html(view.render().el); - setPageTitle($.t('admin.manage-blacklist')); - } - ); + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('admin.manage-blacklist'), + href: "manage/#admin/blacklist" + }]); + + this.updateSidebar('admin/blacklist'); + + var view = new BlackListListView({ + collection: this.blackListList + }); + + view.load(function(collection, response, options) { + $('#content').html(view.render().el); + setPageTitle($.t('admin.manage-blacklist')); + }); } -}); +}); ui.templates.push('resources/template/blacklist.html'); ui.init.push(function(app) { - app.blackListList = new BlackListCollection(); + app.blackListList = new BlackListCollection(); }); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js index fceea87493..e5e42a3107 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/client.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/client.js @@ -16,123 +16,123 @@ *******************************************************************************/ var ClientModel = Backbone.Model.extend({ - idAttribute: "id", - - initialize: function () { - - // bind validation errors to dom elements - // this will display form elements in red if they are not valid - this.bind('error', function(model, errs) { - _.map(errs, function (val, elID) { - $('#' + elID).addClass('error'); - }); - }); - - }, - - // We can pass it default values. - defaults:{ - id:null, - - clientId:null, - clientSecret:null, - redirectUris:[], - clientName:null, - clientUri:null, - logoUri:null, - contacts:[], - tosUri:null, - tokenEndpointAuthMethod:null, - scope:[], - grantTypes:[], - responseTypes:[], - policyUri:null, - - jwksUri:null, - jwks:null, - jwksType:"URI", - - applicationType:null, - sectorIdentifierUri:null, - subjectType:null, - - requestObjectSigningAlg:null, - - userInfoSignedResponseAlg:null, - userInfoEncryptedResponseAlg:null, - userInfoEncryptedResponseEnc:null, - - idTokenSignedResponseAlg:null, - idTokenEncryptedResponseAlg:null, - idTokenEncryptedResponseEnc:null, - - tokenEndpointAuthSigningAlg:null, - - defaultMaxAge:null, - requireAuthTime:false, - defaultACRvalues:null, - - initiateLoginUri:null, - postLogoutRedirectUris:[], - - requestUris:[], - - softwareStatement:null, - softwareId:null, - softwareVersion:null, - - codeChallengeMethod:null, - - authorities:[], - accessTokenValiditySeconds: null, - refreshTokenValiditySeconds: null, - resourceIds:[], - //additionalInformation? - - claimsRedirectUris:[], - - clientDescription:null, - reuseRefreshToken:true, - clearAccessTokensOnRefresh:true, - dynamicallyRegistered:false, - allowIntrospection:false, - idTokenValiditySeconds: null, - deviceCodeValiditySeconds: null, - createdAt:null, - - allowRefresh:false, - displayClientSecret: false, - generateClientSecret: false, - }, - - urlRoot:"api/clients", - - matches:function(term) { - - var matches = []; - - if (term) { - if (this.get('clientId').toLowerCase().indexOf(term.toLowerCase()) != -1) { - matches.push($.t('client.client-table.match.id')); - } - if (this.get('clientName') != null && this.get('clientName').toLowerCase().indexOf(term.toLowerCase()) != -1) { - matches.push($.t('client.client-table.match.name')); - } - if (this.get('clientDescription') != null && this.get('clientDescription').toLowerCase().indexOf(term.toLowerCase()) != -1) { - matches.push($.t('client.client-table.match.description')); - } - if (this.get('clientUri') != null && this.get('clientUri').toLowerCase().indexOf(term.toLowerCase()) != -1) { - matches.push($.t('client.client-table.match.homepage')); - } - if (this.get('policyUri') != null && this.get('policyUri').toLowerCase().indexOf(term.toLowerCase()) != -1) { - matches.push($.t('client.client-table.match.policy')); - } - if (this.get('tosUri') != null && this.get('tosUri').toLowerCase().indexOf(term.toLowerCase()) != -1) { - matches.push($.t('client.client-table.match.terms')); - } - if (this.get('logoUri') != null && this.get('logoUri').toLowerCase().indexOf(term.toLowerCase()) != -1) { - matches.push($.t('client.client-table.match.logo')); - } + idAttribute: "id", + + initialize: function() { + + // bind validation errors to dom elements + // this will display form elements in red if they are not valid + this.bind('error', function(model, errs) { + _.map(errs, function(val, elID) { + $('#' + elID).addClass('error'); + }); + }); + + }, + + // We can pass it default values. + defaults: { + id: null, + + clientId: null, + clientSecret: null, + redirectUris: [], + clientName: null, + clientUri: null, + logoUri: null, + contacts: [], + tosUri: null, + tokenEndpointAuthMethod: null, + scope: [], + grantTypes: [], + responseTypes: [], + policyUri: null, + + jwksUri: null, + jwks: null, + jwksType: "URI", + + applicationType: null, + sectorIdentifierUri: null, + subjectType: null, + + requestObjectSigningAlg: null, + + userInfoSignedResponseAlg: null, + userInfoEncryptedResponseAlg: null, + userInfoEncryptedResponseEnc: null, + + idTokenSignedResponseAlg: null, + idTokenEncryptedResponseAlg: null, + idTokenEncryptedResponseEnc: null, + + tokenEndpointAuthSigningAlg: null, + + defaultMaxAge: null, + requireAuthTime: false, + defaultACRvalues: null, + + initiateLoginUri: null, + postLogoutRedirectUris: [], + + requestUris: [], + + softwareStatement: null, + softwareId: null, + softwareVersion: null, + + codeChallengeMethod: null, + + authorities: [], + accessTokenValiditySeconds: null, + refreshTokenValiditySeconds: null, + resourceIds: [], + // additionalInformation? + + claimsRedirectUris: [], + + clientDescription: null, + reuseRefreshToken: true, + clearAccessTokensOnRefresh: true, + dynamicallyRegistered: false, + allowIntrospection: false, + idTokenValiditySeconds: null, + deviceCodeValiditySeconds: null, + createdAt: null, + + allowRefresh: false, + displayClientSecret: false, + generateClientSecret: false, + }, + + urlRoot: "api/clients", + + matches: function(term) { + + var matches = []; + + if (term) { + if (this.get('clientId').toLowerCase().indexOf(term.toLowerCase()) != -1) { + matches.push($.t('client.client-table.match.id')); + } + if (this.get('clientName') != null && this.get('clientName').toLowerCase().indexOf(term.toLowerCase()) != -1) { + matches.push($.t('client.client-table.match.name')); + } + if (this.get('clientDescription') != null && this.get('clientDescription').toLowerCase().indexOf(term.toLowerCase()) != -1) { + matches.push($.t('client.client-table.match.description')); + } + if (this.get('clientUri') != null && this.get('clientUri').toLowerCase().indexOf(term.toLowerCase()) != -1) { + matches.push($.t('client.client-table.match.homepage')); + } + if (this.get('policyUri') != null && this.get('policyUri').toLowerCase().indexOf(term.toLowerCase()) != -1) { + matches.push($.t('client.client-table.match.policy')); + } + if (this.get('tosUri') != null && this.get('tosUri').toLowerCase().indexOf(term.toLowerCase()) != -1) { + matches.push($.t('client.client-table.match.terms')); + } + if (this.get('logoUri') != null && this.get('logoUri').toLowerCase().indexOf(term.toLowerCase()) != -1) { + matches.push($.t('client.client-table.match.logo')); + } if (this.get('contacts') != null) { var f = _.filter(this.get('contacts'), function(item) { return item.toLowerCase().indexOf(term.toLowerCase()) != -1; @@ -142,7 +142,7 @@ var ClientModel = Backbone.Model.extend({ } } if (this.get('redirectUris') != null) { - var f = _.filter(this.get('redirectUris'), function (item) { + var f = _.filter(this.get('redirectUris'), function(item) { return item.toLowerCase().indexOf(term.toLowerCase()) != -1; }); if (f.length > 0) { @@ -150,7 +150,7 @@ var ClientModel = Backbone.Model.extend({ } } if (this.get('scope') != null) { - var f = _.filter(this.get('scope'), function (item) { + var f = _.filter(this.get('scope'), function(item) { return item.toLowerCase().indexOf(term.toLowerCase()) != -1; }); if (f.length > 0) { @@ -163,29 +163,34 @@ var ClientModel = Backbone.Model.extend({ if (this.get('softwareVersion') != null && this.get('softwareVersion').toLowerCase().indexOf(term.toLowerCase()) != -1) { matches.push($.t('client.client-table.match.software-version')); } - } else { - // there's no search term, we always match - - this.unset('matches', {silent: true}); - //console.log('no term'); - return true; - } - - - var matchString = matches.join(' | '); - - if (matches.length > 0) { - this.set({ - matches: matchString - }, {silent: true}); - - return true; - } else { - this.unset('matches', {silent: true}); - - return false; - } - } + } else { + // there's no search term, we always match + + this.unset('matches', { + silent: true + }); + // console.log('no term'); + return true; + } + + var matchString = matches.join(' | '); + + if (matches.length > 0) { + this.set({ + matches: matchString + }, { + silent: true + }); + + return true; + } else { + this.unset('matches', { + silent: true + }); + + return false; + } + } }); @@ -195,64 +200,66 @@ var RegistrationTokenModel = Backbone.Model.extend({ }); var ClientStatsModel = Backbone.Model.extend({ - urlRoot: 'api/stats/byclientid' + urlRoot: 'api/stats/byclientid' }); var ClientCollection = Backbone.Collection.extend({ - initialize: function() { - //this.fetch(); - }, + initialize: function() { + // this.fetch(); + }, + + model: ClientModel, + url: "api/clients", - model:ClientModel, - url:"api/clients", - - getByClientId: function(clientId) { - var clients = this.where({clientId: clientId}); + getByClientId: function(clientId) { + var clients = this.where({ + clientId: clientId + }); if (clients.length == 1) { return clients[0]; } else { return null; } - } + } }); var ClientView = Backbone.View.extend({ - tagName: 'tr', - - isRendered: false, - - initialize:function (options) { - this.options = options; - - if (!this.template) { - this.template = _.template($('#tmpl-client-table-item').html()); - } - - if (!this.scopeTemplate) { - this.scopeTemplate = _.template($('#tmpl-scope-list').html()); - } - - if (!this.moreInfoTemplate) { - this.moreInfoTemplate = _.template($('#tmpl-client-more-info-block').html()); - } - - if (!this.registrationTokenTemplate) { - this.registrationTokenTemplate = _.template($('#tmpl-client-registration-token').html()); - } - - if (!this.countTemplate) { - this.countTemplate = _.template($('#tmpl-client-count').html()); - } - - this.model.bind('change', this.render, this); - - }, - - render:function (eventName) { - - var creationDate = this.model.get('createdAt'); + tagName: 'tr', + + isRendered: false, + + initialize: function(options) { + this.options = options; + + if (!this.template) { + this.template = _.template($('#tmpl-client-table-item').html()); + } + + if (!this.scopeTemplate) { + this.scopeTemplate = _.template($('#tmpl-scope-list').html()); + } + + if (!this.moreInfoTemplate) { + this.moreInfoTemplate = _.template($('#tmpl-client-more-info-block').html()); + } + + if (!this.registrationTokenTemplate) { + this.registrationTokenTemplate = _.template($('#tmpl-client-registration-token').html()); + } + + if (!this.countTemplate) { + this.countTemplate = _.template($('#tmpl-client-count').html()); + } + + this.model.bind('change', this.render, this); + + }, + + render: function(eventName) { + + var creationDate = this.model.get('createdAt'); var displayCreationDate = $.t('client.client-table.unknown'); var hoverCreationDate = ""; if (creationDate == null || !moment(creationDate).isValid()) { @@ -268,144 +275,176 @@ var ClientView = Backbone.View.extend({ hoverCreationDate = creationDate.format("LLL"); } - - var json = {client: this.model.toJSON(), whiteList: this.options.whiteList, - displayCreationDate: displayCreationDate, hoverCreationDate: hoverCreationDate}; - this.$el.html(this.template(json)); - - $('.scope-list', this.el).html(this.scopeTemplate({scopes: this.model.get('scope'), systemScopes: this.options.systemScopeList})); - - $('.client-more-info-block', this.el).html(this.moreInfoTemplate({client: this.model.toJSON()})); - - $('.clientid-full', this.el).hide(); - - this.$('.dynamically-registered').tooltip({title: $.t('client.client-table.dynamically-registered-tooltip')}); - this.$('.allow-introspection').tooltip({title: $.t('client.client-table.allow-introspection-tooltip')}); - - this.updateMatched(); - this.updateStats(); - - $(this.el).i18n(); - - this.isRendered = true; - - return this; - }, - - updateStats:function(eventName) { - $('.count', this.el).html(this.countTemplate({count: this.options.clientStat.get('approvedSiteCount')})); - }, - - showRegistrationToken:function(e) { - e.preventDefault(); - - $('#modalAlertLabel').html($.t('client.client-form.registration-access-token')); - - var token = new RegistrationTokenModel({clientId: this.model.get('clientId')}); - - var _self = this; - token.fetch({success:function() { - var savedModel = { - clientId: _self.model.get('clientId'), - registrationToken: token.get('value') - }; - - $('#modalAlert .modal-body').html(_self.registrationTokenTemplate(savedModel)); - - $('#modalAlert .modal-body #rotate-token').click(function(e) { - if (confirm($.t('client.client-form.rotate-registration-token-confirm'))) { - token.save(null, {success: function() { - console.log('token:' + token.get('value')); - $('#modalAlert .modal-body #registrationToken').val(token.get('value')); - }, - error: app.errorHandlerView.handleError({message: $.t('client.client-form.rotate-registration-token-error')}) - }); - } - }); - - $('#modalAlert').i18n(); - $('#modalAlert').modal({ - 'backdrop': 'static', - 'keyboard': true, - 'show': true - }); - - }, - error:app.errorHandlerView.handleError({log: "An error occurred when fetching the registration token", message: $.t('client.client-form.registration-token-error')}) - }); - - }, - - updateMatched:function() { - - //console.log(this.model.get('matches')); - - if (this.model.get('matches')) { - $('.matched', this.el).show(); - $('.matched span', this.el).html(this.model.get('matches')); - } else { - $('.matched', this.el).hide(); - } - }, - - events:{ - "click .btn-edit":"editClient", - "click .btn-delete":"deleteClient", - "click .btn-whitelist":"whiteListClient", + var json = { + client: this.model.toJSON(), + whiteList: this.options.whiteList, + displayCreationDate: displayCreationDate, + hoverCreationDate: hoverCreationDate + }; + this.$el.html(this.template(json)); + + $('.scope-list', this.el).html(this.scopeTemplate({ + scopes: this.model.get('scope'), + systemScopes: this.options.systemScopeList + })); + + $('.client-more-info-block', this.el).html(this.moreInfoTemplate({ + client: this.model.toJSON() + })); + + $('.clientid-full', this.el).hide(); + + this.$('.dynamically-registered').tooltip({ + title: $.t('client.client-table.dynamically-registered-tooltip') + }); + this.$('.allow-introspection').tooltip({ + title: $.t('client.client-table.allow-introspection-tooltip') + }); + + this.updateMatched(); + this.updateStats(); + + $(this.el).i18n(); + + this.isRendered = true; + + return this; + }, + + updateStats: function(eventName) { + $('.count', this.el).html(this.countTemplate({ + count: this.options.clientStat.get('approvedSiteCount') + })); + }, + + showRegistrationToken: function(e) { + e.preventDefault(); + + $('#modalAlertLabel').html($.t('client.client-form.registration-access-token')); + + var token = new RegistrationTokenModel({ + clientId: this.model.get('clientId') + }); + + var _self = this; + token.fetch({ + success: function() { + var savedModel = { + clientId: _self.model.get('clientId'), + registrationToken: token.get('value') + }; + + $('#modalAlert .modal-body').html(_self.registrationTokenTemplate(savedModel)); + + $('#modalAlert .modal-body #rotate-token').click(function(e) { + if (confirm($.t('client.client-form.rotate-registration-token-confirm'))) { + token.save(null, { + success: function() { + console.log('token:' + token.get('value')); + $('#modalAlert .modal-body #registrationToken').val(token.get('value')); + }, + error: app.errorHandlerView.handleError({ + message: $.t('client.client-form.rotate-registration-token-error') + }) + }); + } + }); + + $('#modalAlert').i18n(); + $('#modalAlert').modal({ + 'backdrop': 'static', + 'keyboard': true, + 'show': true + }); + + }, + error: app.errorHandlerView.handleError({ + log: "An error occurred when fetching the registration token", + message: $.t('client.client-form.registration-token-error') + }) + }); + + }, + + updateMatched: function() { + + // console.log(this.model.get('matches')); + + if (this.model.get('matches')) { + $('.matched', this.el).show(); + $('.matched span', this.el).html(this.model.get('matches')); + } else { + $('.matched', this.el).hide(); + } + }, + + events: { + "click .btn-edit": "editClient", + "click .btn-delete": "deleteClient", + "click .btn-whitelist": "whiteListClient", 'click .toggleMoreInformation': 'toggleMoreInformation', - "click .clientid-substring":"showClientId", - "click .dynamically-registered": 'showRegistrationToken' - }, - - editClient:function (e) { - e.preventDefault(); - app.navigate('admin/client/' + this.model.id, {trigger: true}); - }, - - whiteListClient:function(e) { - e.preventDefault(); - if (this.options.whiteList == null) { - // create a new one - app.navigate('admin/whitelist/new/' + this.model.get('id'), {trigger: true}); - } else { - // edit the existing one - app.navigate('admin/whitelist/' + this.options.whiteList.get('id'), {trigger: true}); - } - }, - - deleteClient:function (e) { - e.preventDefault(); - - if (confirm($.t('client.client-table.confirm'))) { - var _self = this; - - this.model.destroy({ - dataType: false, processData: false, - success:function () { - _self.$el.fadeTo("fast", 0.00, function () { //fade - $(this).slideUp("fast", function () { //slide up - $(this).remove(); //then remove from the DOM - _self.parentView.togglePlaceholder(); - }); - }); - }, - error:app.errorHandlerView.handleError({log: "An error occurred when deleting a client"}) - }); - - _self.parentView.delegateEvents(); - } - - return false; - }, - - toggleMoreInformation:function(e) { + "click .clientid-substring": "showClientId", + "click .dynamically-registered": 'showRegistrationToken' + }, + + editClient: function(e) { + e.preventDefault(); + app.navigate('admin/client/' + this.model.id, { + trigger: true + }); + }, + + whiteListClient: function(e) { + e.preventDefault(); + if (this.options.whiteList == null) { + // create a new one + app.navigate('admin/whitelist/new/' + this.model.get('id'), { + trigger: true + }); + } else { + // edit the existing one + app.navigate('admin/whitelist/' + this.options.whiteList.get('id'), { + trigger: true + }); + } + }, + + deleteClient: function(e) { + e.preventDefault(); + + if (confirm($.t('client.client-table.confirm'))) { + var _self = this; + + this.model.destroy({ + dataType: false, + processData: false, + success: function() { + _self.$el.fadeTo("fast", 0.00, function() { // fade + $(this).slideUp("fast", function() { // slide up + $(this).remove(); // then remove from the DOM + _self.parentView.togglePlaceholder(); + }); + }); + }, + error: app.errorHandlerView.handleError({ + log: "An error occurred when deleting a client" + }) + }); + + _self.parentView.delegateEvents(); + } + + return false; + }, + + toggleMoreInformation: function(e) { e.preventDefault(); if ($('.moreInformation', this.el).is(':visible')) { // hide it $('.moreInformationContainer', this.el).removeClass('alert').removeClass('alert-info').addClass('muted'); $('.moreInformation', this.el).hide('fast'); $('.toggleMoreInformation i', this.el).attr('class', 'icon-chevron-right'); - + } else { // show it $('.moreInformationContainer', this.el).addClass('alert').addClass('alert-info').removeClass('muted'); @@ -413,134 +452,145 @@ var ClientView = Backbone.View.extend({ $('.toggleMoreInformation i', this.el).attr('class', 'icon-chevron-down'); } }, - - showClientId:function(e) { + + showClientId: function(e) { e.preventDefault(); - + $('.clientid-full', this.el).show(); - + }, - - close:function () { - $(this.el).unbind(); - $(this.el).empty(); - } + + close: function() { + $(this.el).unbind(); + $(this.el).empty(); + } }); var ClientListView = Backbone.View.extend({ - tagName: 'span', - - stats: {}, - - initialize:function (options) { - this.options = options; - this.filteredModel = this.model; - }, - - load:function(callback) { - if (this.model.isFetched && - this.options.whiteListList.isFetched && - this.options.systemScopeList.isFetched) { - callback(); - return; - } - - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t("common.clients") + ' ' + - '' + $.t("whitelist.whitelist") + ' ' + - '' + $.t("common.scopes") + ' ' - ); - - $.when(this.model.fetchIfNeeded({success:function(e) {$('#loading-clients').addClass('label-success');}, error:app.errorHandlerView.handleError()}), - this.options.whiteListList.fetchIfNeeded({success:function(e) {$('#loading-whitelist').addClass('label-success');}, error:app.errorHandlerView.handleError()}), - this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}, error:app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - callback(); - }); - - }, - - events:{ - "click .new-client":"newClient", - "click .refresh-table":"refreshTable", - 'keyup .search-query':'searchTable', - 'click .form-search button':'clearSearch', - 'page .paginator':'changePage' - }, - - newClient:function (e) { - e.preventDefault(); - this.remove(); - app.navigate('admin/client/new', {trigger: true}); - }, - - render:function (eventName) { - - // append and render table structure - $(this.el).html($('#tmpl-client-table').html()); - - this.renderInner(); - $(this.el).i18n(); - return this; - }, - - renderInner:function(eventName) { - - // set up the rows to render - // (note that this doesn't render until visibility is determined in togglePlaceholder) - - _.each(this.filteredModel.models, function (client, index) { - var clientStat = this.getStat(client.get('clientId')); - var view = new ClientView({ - model:client, - clientStat:clientStat, + tagName: 'span', + + stats: {}, + + initialize: function(options) { + this.options = options; + this.filteredModel = this.model; + }, + + load: function(callback) { + if (this.model.isFetched && this.options.whiteListList.isFetched && this.options.systemScopeList.isFetched) { + callback(); + return; + } + + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t("common.clients") + ' ' + '' + $.t("whitelist.whitelist") + ' ' + '' + $.t("common.scopes") + ' '); + + $.when(this.model.fetchIfNeeded({ + success: function(e) { + $('#loading-clients').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.whiteListList.fetchIfNeeded({ + success: function(e) { + $('#loading-whitelist').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.systemScopeList.fetchIfNeeded({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + callback(); + }); + + }, + + events: { + "click .new-client": "newClient", + "click .refresh-table": "refreshTable", + 'keyup .search-query': 'searchTable', + 'click .form-search button': 'clearSearch', + 'page .paginator': 'changePage' + }, + + newClient: function(e) { + e.preventDefault(); + this.remove(); + app.navigate('admin/client/new', { + trigger: true + }); + }, + + render: function(eventName) { + + // append and render table structure + $(this.el).html($('#tmpl-client-table').html()); + + this.renderInner(); + $(this.el).i18n(); + return this; + }, + + renderInner: function(eventName) { + + // set up the rows to render + // (note that this doesn't render until visibility is determined in + // togglePlaceholder) + + _.each(this.filteredModel.models, function(client, index) { + var clientStat = this.getStat(client.get('clientId')); + var view = new ClientView({ + model: client, + clientStat: clientStat, systemScopeList: this.options.systemScopeList, whiteList: this.options.whiteListList.getByClientId(client.get('clientId')) }); - view.parentView = this; - //var element = view.render().el; - var element = view.el; - $("#client-table",this.el).append(element); - this.addView(client.get('id'), view); - }, this); - - this.togglePlaceholder(); - }, - - views:{}, - - addView:function(index, view) { - this.views[index] = view; - }, - - getView:function(index) { - return this.views[index]; - }, - - getStat:function(index) { + view.parentView = this; + // var element = view.render().el; + var element = view.el; + $("#client-table", this.el).append(element); + this.addView(client.get('id'), view); + }, this); + + this.togglePlaceholder(); + }, + + views: {}, + + addView: function(index, view) { + this.views[index] = view; + }, + + getView: function(index) { + return this.views[index]; + }, + + getStat: function(index) { if (!this.stats[index]) { - this.stats[index] = new ClientStatsModel({id: index}); + this.stats[index] = new ClientStatsModel({ + id: index + }); } return this.stats[index]; }, - - togglePlaceholder:function() { - // set up pagination - var numPages = Math.ceil(this.filteredModel.length / 10); - if (numPages > 1) { - $('.paginator', this.el).show(); - $('.paginator', this.el).bootpag({ - total: numPages, - maxVisible: 10, - leaps: false, - page: 1 - }); - } else { - $('.paginator', this.el).hide(); - } + + togglePlaceholder: function() { + // set up pagination + var numPages = Math.ceil(this.filteredModel.length / 10); + if (numPages > 1) { + $('.paginator', this.el).show(); + $('.paginator', this.el).bootpag({ + total: numPages, + maxVisible: 10, + leaps: false, + page: 1 + }); + } else { + $('.paginator', this.el).hide(); + } if (this.filteredModel.length > 0) { this.changePage(undefined, 1); @@ -561,867 +611,947 @@ var ClientListView = Backbone.View.extend({ } } }, - - changePage:function(event, num) { + + changePage: function(event, num) { console.log('Page changed: ' + num); - - $('.paginator', this.el).bootpag({page:num}); + + $('.paginator', this.el).bootpag({ + page: num + }); var _self = this; - - _.each(this.filteredModel.models, function (client, index) { - var view = _self.getView(client.get('id')); - if (!view) { - console.log('Error: no view for client ' + client.get('id')); - return; - } - - // only show/render clients on the current page - - console.log(':: ' + index + ' ' + num + ' ' + Math.ceil((index + 1) / 10) != num); - - if (Math.ceil((index + 1) / 10) != num) { - $(view.el).hide(); - } else { - if (!view.isRendered) { - view.render(); - var clientStat = view.options.clientStat; - - // load and display the stats - $.when(clientStat.fetchIfNeeded({ - success:function(e) { - - }, - error:app.errorHandlerView.handleError()})) - .done(function(e) { - view.updateStats(); - }); - } - $(view.el).show(); - } - }); - - /* - $('#client-table tbody tr', this.el).each(function(index, element) { + + _.each(this.filteredModel.models, function(client, index) { + var view = _self.getView(client.get('id')); + if (!view) { + console.log('Error: no view for client ' + client.get('id')); + return; + } + + // only show/render clients on the current page + + console.log(':: ' + index + ' ' + num + ' ' + Math.ceil((index + 1) / 10) != num); + if (Math.ceil((index + 1) / 10) != num) { - // hide the element - $(element).hide(); - } else { - // show the element - $(element).show(); - } + $(view.el).hide(); + } else { + if (!view.isRendered) { + view.render(); + var clientStat = view.options.clientStat; + + // load and display the stats + $.when(clientStat.fetchIfNeeded({ + success: function(e) { + + }, + error: app.errorHandlerView.handleError() + })).done(function(e) { + view.updateStats(); + }); + } + $(view.el).show(); + } + }); + + /* + * $('#client-table tbody tr', this.el).each(function(index, element) { + * if (Math.ceil((index + 1) / 10) != num) { // hide the element + * $(element).hide(); } else { // show the element $(element).show(); } + * }); + */ + }, + + refreshTable: function(e) { + e.preventDefault(); + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t("common.clients") + ' ' + '' + $.t("whitelist.whitelist") + ' ' + '' + $.t("common.scopes") + ' '); + + var _self = this; + $.when(this.model.fetch({ + success: function(e) { + $('#loading-clients').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.whiteListList.fetch({ + success: function(e) { + $('#loading-whitelist').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.systemScopeList.fetch({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + _self.render(); }); - */ }, - - refreshTable:function(e) { - e.preventDefault(); - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t("common.clients") + ' ' + - '' + $.t("whitelist.whitelist") + ' ' + - '' + $.t("common.scopes") + ' ' - ); - - var _self = this; - $.when(this.model.fetch({success:function(e) {$('#loading-clients').addClass('label-success');}, error:app.errorHandlerView.handleError()}), - this.options.whiteListList.fetch({success:function(e) {$('#loading-whitelist').addClass('label-success');}, error:app.errorHandlerView.handleError()}), - this.options.systemScopeList.fetch({success:function(e) {$('#loading-scopes').addClass('label-success');}, error:app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - _self.render(); - }); - }, - - searchTable:function(e) { - var term = $('.search-query', this.el).val(); - + + searchTable: function(e) { + var term = $('.search-query', this.el).val(); + this.filteredModel = new ClientCollection(this.model.filter(function(client) { return client.matches(term); })); - - // clear out the table - $('tbody', this.el).html(''); - - // re-render the table - this.renderInner(); - - }, - - clearSearch:function(e) { - $('.search-query', this.el).val(''); - this.searchTable(); - } - - + + // clear out the table + $('tbody', this.el).html(''); + + // re-render the table + this.renderInner(); + + }, + + clearSearch: function(e) { + $('.search-query', this.el).val(''); + this.searchTable(); + } + }); var ClientFormView = Backbone.View.extend({ - tagName:"span", - - initialize:function (options) { - this.options = options; - - if (!this.template) { - this.template = _.template($('#tmpl-client-form').html()); - } - - if (!this.clientSavedTemplate) { - this.clientSavedTemplate = _.template($('#tmpl-client-saved').html()); - } - - this.redirectUrisCollection = new Backbone.Collection(); - this.scopeCollection = new Backbone.Collection(); - this.contactsCollection = new Backbone.Collection(); - this.defaultACRvaluesCollection = new Backbone.Collection(); - this.requestUrisCollection = new Backbone.Collection(); - this.postLogoutRedirectUrisCollection = new Backbone.Collection(); - this.claimsRedirectUrisCollection = new Backbone.Collection(); - // TODO: add Spring authorities collection and resource IDs collection? - - // collection of sub-views that need to be sync'd on save - this.listWidgetViews = []; - }, - - events:{ - "click .btn-save":"saveClient", - "click #allowRefresh" : "toggleRefreshTokenTimeout", - "click #disableAccessTokenTimeout" : function() { - $("#access-token-timeout-time", this.$el).prop('disabled',!$("#access-token-timeout-time", this.$el).prop('disabled')); - $("#access-token-timeout-unit", this.$el).prop('disabled',!$("#access-token-timeout-unit", this.$el).prop('disabled')); - document.getElementById("access-token-timeout-time").value = ''; - }, - "click #disableRefreshTokenTimeout" : function() { - $("#refresh-token-timeout-time", this.$el).prop('disabled',!$("#refresh-token-timeout-time", this.$el).prop('disabled')); - $("#refresh-token-timeout-unit", this.$el).prop('disabled',!$("#refresh-token-timeout-unit", this.$el).prop('disabled')); - document.getElementById("refresh-token-timeout-time").value = ''; - }, - "click .btn-cancel":"cancel", - "change #tokenEndpointAuthMethod input:radio":"toggleClientCredentials", - "change #displayClientSecret":"toggleDisplayClientSecret", - "change #generateClientSecret":"toggleGenerateClientSecret", - "change #logoUri input":"previewLogo", - "change #jwkSelector input:radio":"toggleJWKSetType" - }, - - cancel:function(e) { - e.preventDefault(); - app.navigate('admin/clients', {trigger: true}); - }, - - load:function(callback) { - if (this.model.isFetched && - this.options.systemScopeList.isFetched) { - callback(); - return; - } - - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t('common.clients') + ' ' + - '' + $.t("common.scopes") + ' ' - ); - - $.when(this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}, error:app.errorHandlerView.handleError()}), - this.model.fetchIfNeeded({success:function(e) {$('#loading-clients').addClass('label-success');}, error:app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - callback(); - }); + tagName: "span", + + initialize: function(options) { + this.options = options; + + if (!this.template) { + this.template = _.template($('#tmpl-client-form').html()); + } + + if (!this.clientSavedTemplate) { + this.clientSavedTemplate = _.template($('#tmpl-client-saved').html()); + } + + this.redirectUrisCollection = new Backbone.Collection(); + this.scopeCollection = new Backbone.Collection(); + this.contactsCollection = new Backbone.Collection(); + this.defaultACRvaluesCollection = new Backbone.Collection(); + this.requestUrisCollection = new Backbone.Collection(); + this.postLogoutRedirectUrisCollection = new Backbone.Collection(); + this.claimsRedirectUrisCollection = new Backbone.Collection(); + // TODO: add Spring authorities collection and resource IDs collection? + + // collection of sub-views that need to be sync'd on save + this.listWidgetViews = []; + }, + + events: { + "click .btn-save": "saveClient", + "click #allowRefresh": "toggleRefreshTokenTimeout", + "click #disableAccessTokenTimeout": function() { + $("#access-token-timeout-time", this.$el).prop('disabled', !$("#access-token-timeout-time", this.$el).prop('disabled')); + $("#access-token-timeout-unit", this.$el).prop('disabled', !$("#access-token-timeout-unit", this.$el).prop('disabled')); + document.getElementById("access-token-timeout-time").value = ''; + }, + "click #disableRefreshTokenTimeout": function() { + $("#refresh-token-timeout-time", this.$el).prop('disabled', !$("#refresh-token-timeout-time", this.$el).prop('disabled')); + $("#refresh-token-timeout-unit", this.$el).prop('disabled', !$("#refresh-token-timeout-unit", this.$el).prop('disabled')); + document.getElementById("refresh-token-timeout-time").value = ''; + }, + "click .btn-cancel": "cancel", + "change #tokenEndpointAuthMethod input:radio": "toggleClientCredentials", + "change #displayClientSecret": "toggleDisplayClientSecret", + "change #generateClientSecret": "toggleGenerateClientSecret", + "change #logoUri input": "previewLogo", + "change #jwkSelector input:radio": "toggleJWKSetType" + }, + + cancel: function(e) { + e.preventDefault(); + app.navigate('admin/clients', { + trigger: true + }); + }, + + load: function(callback) { + if (this.model.isFetched && this.options.systemScopeList.isFetched) { + callback(); + return; + } + + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t('common.clients') + ' ' + '' + $.t("common.scopes") + ' '); + + $.when(this.options.systemScopeList.fetchIfNeeded({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.model.fetchIfNeeded({ + success: function(e) { + $('#loading-clients').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + callback(); + }); + }, + + toggleRefreshTokenTimeout: function() { + $("#refreshTokenValidityTime", this.$el).toggle(); }, - - toggleRefreshTokenTimeout:function () { - $("#refreshTokenValidityTime", this.$el).toggle(); - }, - - previewLogo:function() { - if ($('#logoUri input', this.el).val()) { - $('#logoPreview', this.el).empty(); - $('#logoPreview', this.el).attr('src', $('#logoUri input', this.el).val()); - } else { - //$('#logoBlock', this.el).hide(); - $('#logoPreview', this.el).attr('src', 'resources/images/logo_placeholder.gif'); - } - }, - - /** - * Set up the form based on the current state of the tokenEndpointAuthMethod parameter - * @param event - */ - toggleClientCredentials:function() { - - var tokenEndpointAuthMethod = $('#tokenEndpointAuthMethod input', this.el).filter(':checked').val(); - - if (tokenEndpointAuthMethod == 'SECRET_BASIC' - || tokenEndpointAuthMethod == 'SECRET_POST' - || tokenEndpointAuthMethod == 'SECRET_JWT') { - - // client secret is required, show all the bits - $('#clientSecretPanel', this.el).show(); - // this function sets up the display portions - this.toggleGenerateClientSecret(); - } else { - // no client secret, hide all the bits - $('#clientSecretPanel', this.el).hide(); - } - - // show or hide the signing algorithm method depending on what's selected - if (tokenEndpointAuthMethod == 'PRIVATE_KEY' - || tokenEndpointAuthMethod == 'SECRET_JWT') { - $('#tokenEndpointAuthSigningAlg', this.el).show(); - } else { - $('#tokenEndpointAuthSigningAlg', this.el).hide(); - } - }, - - /** - * Set up the form based on the JWK Set selector - */ - toggleJWKSetType:function() { - var jwkSelector = $('#jwkSelector input:radio', this.el).filter(':checked').val(); - - if (jwkSelector == 'URI') { - $('#jwksUri', this.el).show(); - $('#jwks', this.el).hide(); - } else if (jwkSelector == 'VAL') { - $('#jwksUri', this.el).hide(); - $('#jwks', this.el).show(); - } else { - $('#jwksUri', this.el).hide(); - $('#jwks', this.el).hide(); - } - - }, - - /** - * Set up the form based on the "Generate" checkbox - * @param event - */ - toggleGenerateClientSecret:function() { - - if ($('#generateClientSecret input', this.el).is(':checked')) { - // show the "generated" block, hide the "display" checkbox - $('#displayClientSecret', this.el).hide(); - $('#clientSecret', this.el).hide(); - $('#clientSecretGenerated', this.el).show(); - $('#clientSecretHidden', this.el).hide(); - } else { - // show the display checkbox, fall back to the "display" logic - $('#displayClientSecret', this.el).show(); - this.toggleDisplayClientSecret(); - } - }, - - /** - * Handle whether or not to display the client secret - * @param event - */ - toggleDisplayClientSecret:function() { - - if ($('#displayClientSecret input').is(':checked')) { - // want to display it - $('#clientSecret', this.el).show(); - $('#clientSecretHidden', this.el).hide(); - $('#clientSecretGenerated', this.el).hide(); - } else { - // want to hide it - $('#clientSecret', this.el).hide(); - $('#clientSecretHidden', this.el).show(); - $('#clientSecretGenerated', this.el).hide(); - } - }, - - // rounds down to the nearest integer value in seconds. - getFormTokenNumberValue:function(value, timeUnit) { - if (value == "") { - return null; - } else if (timeUnit == 'hours') { - return parseInt(parseFloat(value) * 3600); - } else if (timeUnit == 'minutes') { - return parseInt(parseFloat(value) * 60); - } else { // seconds - return parseInt(value); - } - }, - - // returns "null" if given the value "default" as a string, otherwise returns input value. useful for parsing the JOSE algorithm dropdowns - defaultToNull:function(value) { - if (value == 'default') { - return null; - } else { - return value; - } - }, - - // returns "null" if the given value is falsy - emptyToNull:function(value) { - if (value) { - return value; - } else { - return null; - } - }, - - disableUnsupportedJOSEItems:function(serverSupported, query) { - var supported = ['default']; - if (serverSupported) { - supported = _.union(supported, serverSupported); - } - $(query, this.$el).each(function(idx) { - if(_.contains(supported, $(this).val())) { - $(this).prop('disabled', false); - } else { - $(this).prop('disabled', true); - } - }); - - }, - - // maps from a form-friendly name to the real grant parameter name - grantMap:{ - 'authorization_code': 'authorization_code', - 'password': 'password', - 'implicit': 'implicit', - 'client_credentials': 'client_credentials', - 'redelegate': 'urn:ietf:params:oauth:grant_type:redelegate', - 'refresh_token': 'refresh_token', - 'device': 'urn:ietf:params:oauth:grant-type:device_code' - }, - - // maps from a form-friendly name to the real response type parameter name - responseMap:{ - 'code': 'code', - 'token': 'token', - 'idtoken': 'id_token', - 'token-idtoken': 'token id_token', - 'code-idtoken': 'code id_token', - 'code-token': 'code token', - 'code-token-idtoken': 'code token id_token' - }, - - saveClient:function (event) { - - $('.control-group').removeClass('error'); - - // sync any leftover collection items - _.each(this.listWidgetViews, function(v) { - v.addItem($.Event('click')); - }); - - // build the scope object - var scopes = this.scopeCollection.pluck("item"); - - // build the grant type object - var grantTypes = []; - $.each(this.grantMap, function(index,type) { - if ($('#grantTypes-' + index).is(':checked')) { - grantTypes.push(type); - } - }); - - // build the response type object - var responseTypes = []; - $.each(this.responseMap, function(index,type) { - if ($('#responseTypes-' + index).is(':checked')) { - responseTypes.push(type); - } - }); - - var generateClientSecret = $('#generateClientSecret input').is(':checked'); - var clientSecret = null; - - var tokenEndpointAuthMethod = $('#tokenEndpointAuthMethod input').filter(':checked').val(); - - // whether or not the client secret changed - var secretChanged = false; - - if (tokenEndpointAuthMethod == 'SECRET_BASIC' - || tokenEndpointAuthMethod == 'SECRET_POST' - || tokenEndpointAuthMethod == 'SECRET_JWT') { - - if (!generateClientSecret) { - // if it's required but we're not generating it, send the value to preserve it - clientSecret = $('#clientSecret input').val(); - - // if it's not the same as before, offer to display it - if (clientSecret != this.model.get('clientSecret')) { - secretChanged = true; - } - } else { - // it's being generated anew - secretChanged = true; - } - } - - var accessTokenValiditySeconds = null; - if (!$('disableAccessTokenTimeout').is(':checked')) { - accessTokenValiditySeconds = this.getFormTokenNumberValue($('#accessTokenValidityTime input[type=text]').val(), $('#accessTokenValidityTime select').val()); - } - - var idTokenValiditySeconds = this.getFormTokenNumberValue($('#idTokenValidityTime input[type=text]').val(), $('#idTokenValidityTime select').val()); - - var deviceCodeValiditySeconds = this.getFormTokenNumberValue($('#deviceCodeValidityTime input[type=text]').val(), $('#deviceCodeValidityTime select').val()); - - var refreshTokenValiditySeconds = null; - if ($('#allowRefresh').is(':checked')) { - - if ($.inArray('refresh_token', grantTypes) == -1) { - grantTypes.push('refresh_token'); - } - - if ($.inArray('offline_access', scopes) == -1) { - scopes.push("offline_access"); - } - - if (!$('disableRefreshTokenTimeout').is(':checked')) { - refreshTokenValiditySeconds = this.getFormTokenNumberValue($('#refreshTokenValidityTime input[type=text]').val(), $('#refreshTokenValidityTime select').val()); - } - } - - // make sure that the subject identifier is consistent with the redirect URIs - var subjectType = $('#subjectType input').filter(':checked').val(); - var redirectUris = this.redirectUrisCollection.pluck("item"); - var sectorIdentifierUri = $('#sectorIdentifierUri input').val(); - if (subjectType == 'PAIRWISE' && redirectUris.length > 1 && sectorIdentifierUri == '') { - //Display an alert with an error message - app.errorHandlerView.showErrorMessage($.t("client.client-form.error.consistency"), $.t("client.client-form.error.pairwise-sector")); + + previewLogo: function() { + if ($('#logoUri input', this.el).val()) { + $('#logoPreview', this.el).empty(); + $('#logoPreview', this.el).attr('src', $('#logoUri input', this.el).val()); + } else { + // $('#logoBlock', this.el).hide(); + $('#logoPreview', this.el).attr('src', 'resources/images/logo_placeholder.gif'); + } + }, + + /** + * Set up the form based on the current state of the tokenEndpointAuthMethod + * parameter + * + * @param event + */ + toggleClientCredentials: function() { + + var tokenEndpointAuthMethod = $('#tokenEndpointAuthMethod input', this.el).filter(':checked').val(); + + if (tokenEndpointAuthMethod == 'SECRET_BASIC' || tokenEndpointAuthMethod == 'SECRET_POST' || tokenEndpointAuthMethod == 'SECRET_JWT') { + + // client secret is required, show all the bits + $('#clientSecretPanel', this.el).show(); + // this function sets up the display portions + this.toggleGenerateClientSecret(); + } else { + // no client secret, hide all the bits + $('#clientSecretPanel', this.el).hide(); + } + + // show or hide the signing algorithm method depending on what's + // selected + if (tokenEndpointAuthMethod == 'PRIVATE_KEY' || tokenEndpointAuthMethod == 'SECRET_JWT') { + $('#tokenEndpointAuthSigningAlg', this.el).show(); + } else { + $('#tokenEndpointAuthSigningAlg', this.el).hide(); + } + }, + + /** + * Set up the form based on the JWK Set selector + */ + toggleJWKSetType: function() { + var jwkSelector = $('#jwkSelector input:radio', this.el).filter(':checked').val(); + + if (jwkSelector == 'URI') { + $('#jwksUri', this.el).show(); + $('#jwks', this.el).hide(); + } else if (jwkSelector == 'VAL') { + $('#jwksUri', this.el).hide(); + $('#jwks', this.el).show(); + } else { + $('#jwksUri', this.el).hide(); + $('#jwks', this.el).hide(); + } + + }, + + /** + * Set up the form based on the "Generate" checkbox + * + * @param event + */ + toggleGenerateClientSecret: function() { + + if ($('#generateClientSecret input', this.el).is(':checked')) { + // show the "generated" block, hide the "display" checkbox + $('#displayClientSecret', this.el).hide(); + $('#clientSecret', this.el).hide(); + $('#clientSecretGenerated', this.el).show(); + $('#clientSecretHidden', this.el).hide(); + } else { + // show the display checkbox, fall back to the "display" logic + $('#displayClientSecret', this.el).show(); + this.toggleDisplayClientSecret(); + } + }, + + /** + * Handle whether or not to display the client secret + * + * @param event + */ + toggleDisplayClientSecret: function() { + + if ($('#displayClientSecret input').is(':checked')) { + // want to display it + $('#clientSecret', this.el).show(); + $('#clientSecretHidden', this.el).hide(); + $('#clientSecretGenerated', this.el).hide(); + } else { + // want to hide it + $('#clientSecret', this.el).hide(); + $('#clientSecretHidden', this.el).show(); + $('#clientSecretGenerated', this.el).hide(); + } + }, + + // rounds down to the nearest integer value in seconds. + getFormTokenNumberValue: function(value, timeUnit) { + if (value == "") { + return null; + } else if (timeUnit == 'hours') { + return parseInt(parseFloat(value) * 3600); + } else if (timeUnit == 'minutes') { + return parseInt(parseFloat(value) * 60); + } else { // seconds + return parseInt(value); + } + }, + + // returns "null" if given the value "default" as a string, otherwise + // returns input value. useful for parsing the JOSE algorithm dropdowns + defaultToNull: function(value) { + if (value == 'default') { + return null; + } else { + return value; + } + }, + + // returns "null" if the given value is falsy + emptyToNull: function(value) { + if (value) { + return value; + } else { + return null; + } + }, + + disableUnsupportedJOSEItems: function(serverSupported, query) { + var supported = ['default']; + if (serverSupported) { + supported = _.union(supported, serverSupported); + } + $(query, this.$el).each(function(idx) { + if (_.contains(supported, $(this).val())) { + $(this).prop('disabled', false); + } else { + $(this).prop('disabled', true); + } + }); + + }, + + // maps from a form-friendly name to the real grant parameter name + grantMap: { + 'authorization_code': 'authorization_code', + 'password': 'password', + 'implicit': 'implicit', + 'client_credentials': 'client_credentials', + 'redelegate': 'urn:ietf:params:oauth:grant_type:redelegate', + 'refresh_token': 'refresh_token', + 'device': 'urn:ietf:params:oauth:grant-type:device_code' + }, + + // maps from a form-friendly name to the real response type parameter name + responseMap: { + 'code': 'code', + 'token': 'token', + 'idtoken': 'id_token', + 'token-idtoken': 'token id_token', + 'code-idtoken': 'code id_token', + 'code-token': 'code token', + 'code-token-idtoken': 'code token id_token' + }, + + saveClient: function(event) { + + $('.control-group').removeClass('error'); + + // sync any leftover collection items + _.each(this.listWidgetViews, function(v) { + v.addItem($.Event('click')); + }); + + // build the scope object + var scopes = this.scopeCollection.pluck("item"); + + // build the grant type object + var grantTypes = []; + $.each(this.grantMap, function(index, type) { + if ($('#grantTypes-' + index).is(':checked')) { + grantTypes.push(type); + } + }); + + // build the response type object + var responseTypes = []; + $.each(this.responseMap, function(index, type) { + if ($('#responseTypes-' + index).is(':checked')) { + responseTypes.push(type); + } + }); + + var generateClientSecret = $('#generateClientSecret input').is(':checked'); + var clientSecret = null; + + var tokenEndpointAuthMethod = $('#tokenEndpointAuthMethod input').filter(':checked').val(); + + // whether or not the client secret changed + var secretChanged = false; + + if (tokenEndpointAuthMethod == 'SECRET_BASIC' || tokenEndpointAuthMethod == 'SECRET_POST' || tokenEndpointAuthMethod == 'SECRET_JWT') { + + if (!generateClientSecret) { + // if it's required but we're not generating it, send the value + // to preserve it + clientSecret = $('#clientSecret input').val(); + + // if it's not the same as before, offer to display it + if (clientSecret != this.model.get('clientSecret')) { + secretChanged = true; + } + } else { + // it's being generated anew + secretChanged = true; + } + } + + var accessTokenValiditySeconds = null; + if (!$('disableAccessTokenTimeout').is(':checked')) { + accessTokenValiditySeconds = this.getFormTokenNumberValue($('#accessTokenValidityTime input[type=text]').val(), $('#accessTokenValidityTime select').val()); + } + + var idTokenValiditySeconds = this.getFormTokenNumberValue($('#idTokenValidityTime input[type=text]').val(), $('#idTokenValidityTime select').val()); + + var deviceCodeValiditySeconds = this.getFormTokenNumberValue($('#deviceCodeValidityTime input[type=text]').val(), $('#deviceCodeValidityTime select').val()); + + var refreshTokenValiditySeconds = null; + if ($('#allowRefresh').is(':checked')) { + + if ($.inArray('refresh_token', grantTypes) == -1) { + grantTypes.push('refresh_token'); + } + + if ($.inArray('offline_access', scopes) == -1) { + scopes.push("offline_access"); + } + + if (!$('disableRefreshTokenTimeout').is(':checked')) { + refreshTokenValiditySeconds = this.getFormTokenNumberValue($('#refreshTokenValidityTime input[type=text]').val(), $('#refreshTokenValidityTime select').val()); + } + } + + // make sure that the subject identifier is consistent with the redirect + // URIs + var subjectType = $('#subjectType input').filter(':checked').val(); + var redirectUris = this.redirectUrisCollection.pluck("item"); + var sectorIdentifierUri = $('#sectorIdentifierUri input').val(); + if (subjectType == 'PAIRWISE' && redirectUris.length > 1 && sectorIdentifierUri == '') { + // Display an alert with an error message + app.errorHandlerView.showErrorMessage($.t("client.client-form.error.consistency"), $.t("client.client-form.error.pairwise-sector")); return false; - - } - - // process the JWKS - var jwksUri = null; - var jwks = null; - var jwkSelector = $('#jwkSelector input:radio', this.el).filter(':checked').val(); - - if (jwkSelector == 'URI') { - jwksUri = $('#jwksUri input').val(); - jwks = null; - } else if (jwkSelector == 'VAL') { - jwksUri = null; - try { - jwks = JSON.parse($('#jwks textarea').val()); - } catch (e) { - console.log("An error occurred when parsing the JWK Set"); - app.errorHandlerView.showErrorMessage($.t("client.client-form.error.jwk-set"), $.t("client.client-form.error.jwk-set-parse")); - return false; - } - } else { - jwksUri = null; - jwks = null; - } - - - - var attrs = { - clientName:this.emptyToNull($('#clientName input').val()), - clientId:this.emptyToNull($('#clientId input').val()), - clientSecret: clientSecret, - generateClientSecret:generateClientSecret, - redirectUris: redirectUris, - clientDescription:this.emptyToNull($('#clientDescription textarea').val()), - logoUri:this.emptyToNull($('#logoUri input').val()), - grantTypes: grantTypes, - accessTokenValiditySeconds: accessTokenValiditySeconds, - refreshTokenValiditySeconds: refreshTokenValiditySeconds, - idTokenValiditySeconds: idTokenValiditySeconds, - deviceCodeValiditySeconds: deviceCodeValiditySeconds, - allowRefresh: $('#allowRefresh').is(':checked'), - allowIntrospection: $('#allowIntrospection input').is(':checked'), - scope: scopes, - tosUri: this.emptyToNull($('#tosUri input').val()), - policyUri: this.emptyToNull($('#policyUri input').val()), - clientUri: this.emptyToNull($('#clientUri input').val()), - applicationType: $('#applicationType input').filter(':checked').val(), - jwksUri: jwksUri, - jwks: jwks, - subjectType: subjectType, - softwareStatement: this.emptyToNull($('#softwareStatement textarea').val()), - softwareId: this.emptyToNull($('#softwareId input').val()), - softwareVersion: this.emptyToNull($('#softwareVersion input').val()), - tokenEndpointAuthMethod: tokenEndpointAuthMethod, - responseTypes: responseTypes, - sectorIdentifierUri: sectorIdentifierUri, - initiateLoginUri: this.emptyToNull($('#initiateLoginUri input').val()), - postLogoutRedirectUris: this.postLogoutRedirectUrisCollection.pluck('item'), - claimsRedirectUris: this.claimsRedirectUrisCollection.pluck('item'), - reuseRefreshToken: $('#reuseRefreshToken').is(':checked'), - clearAccessTokensOnRefresh: $('#clearAccessTokensOnRefresh').is(':checked'), - requireAuthTime: $('#requireAuthTime input').is(':checked'), - defaultMaxAge: parseInt($('#defaultMaxAge input').val()), - contacts: this.contactsCollection.pluck('item'), - requestUris: this.requestUrisCollection.pluck('item'), - defaultACRvalues: this.defaultACRvaluesCollection.pluck('item'), - requestObjectSigningAlg: this.defaultToNull($('#requestObjectSigningAlg select').val()), - userInfoSignedResponseAlg: this.defaultToNull($('#userInfoSignedResponseAlg select').val()), - userInfoEncryptedResponseAlg: this.defaultToNull($('#userInfoEncryptedResponseAlg select').val()), - userInfoEncryptedResponseEnc: this.defaultToNull($('#userInfoEncryptedResponseEnc select').val()), - idTokenSignedResponseAlg: this.defaultToNull($('#idTokenSignedResponseAlg select').val()), - idTokenEncryptedResponseAlg: this.defaultToNull($('#idTokenEncryptedResponseAlg select').val()), - idTokenEncryptedResponseEnc: this.defaultToNull($('#idTokenEncryptedResponseEnc select').val()), - tokenEndpointAuthSigningAlg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val()), - codeChallengeMethod: this.defaultToNull($('#codeChallengeMethod select').val()) - }; - - // post-validate - if (attrs["allowRefresh"] == false) { - attrs["refreshTokenValiditySeconds"] = null; - } - - if ($('#disableAccessTokenTimeout').is(':checked')) { - attrs["accessTokenValiditySeconds"] = null; - } - - if ($('#disableRefreshTokenTimeout').is(':checked')) { - attrs["refreshTokenValiditySeconds"] = null; - } - - // set all empty strings to nulls - for (var key in attrs) { - if (attrs[key] === "") { - attrs[key] = null; - } - } - - var _self = this; - this.model.save(attrs, { - success:function () { - - $('#modalAlertLabel').html($.t('client.client-form.saved.saved')); - - var savedModel = { - clientId: _self.model.get('clientId'), - clientSecret: _self.model.get('clientSecret'), - secretChanged: secretChanged - }; - - $('#modalAlert div.modal-header').html($.t('client.client-form.saved.saved')); - - $('#modalAlert .modal-body').html(_self.clientSavedTemplate(savedModel)); - - $('#modalAlert .modal-body #savedClientSecret').hide(); - - $('#modalAlert').on('click', '#clientSaveShow', function(event) { - event.preventDefault(); - $('#clientSaveShow').hide(); - $('#savedClientSecret').show(); - }); - - $('#modalAlert').i18n(); - $('#modalAlert').modal({ - 'backdrop': 'static', - 'keyboard': true, - 'show': true - }); - - app.clientList.add(_self.model); - app.navigate('admin/clients', {trigger:true}); - }, - error:app.errorHandlerView.handleError({log: "An error occurred when saving a client"}) - }); - - return false; - }, - - render:function (eventName) { - - var data = {client: this.model.toJSON(), heartMode: heartMode}; - $(this.el).html(this.template(data)); - - var _self = this; - - // clear the sub-view collection - this.listWidgetViews = []; - - // build and bind registered redirect URI collection and view - _.each(this.model.get("redirectUris"), function (redirectUri) { - _self.redirectUrisCollection.add(new URIModel({item:redirectUri})); - }); - - - var redirUriView = new ListWidgetView({ - type:'uri', - placeholder: 'https://', - helpBlockText: $.t('client.client-form.redirect-uris-help'), - collection: this.redirectUrisCollection}); - $("#redirectUris .controls",this.el).html(redirUriView.render().el); - this.listWidgetViews.push(redirUriView); - - // build and bind scopes - _.each(this.model.get("scope"), function (scope) { - _self.scopeCollection.add(new Backbone.Model({item:scope})); - }); - - var scopeView = new ListWidgetView({ - placeholder: $.t('client.client-form.scope-placeholder'), - autocomplete: _.uniq(_.flatten(this.options.systemScopeList.pluck("value"))), - helpBlockText: $.t('client.client-form.scope-help'), - collection: this.scopeCollection}); - $("#scope .controls",this.el).html(scopeView.render().el); - this.listWidgetViews.push(scopeView); - - // build and bind contacts - _.each(this.model.get('contacts'), function (contact) { - _self.contactsCollection.add(new Backbone.Model({item:contact})); - }); - - var contactsView = new ListWidgetView({ - placeholder: $.t("client.client-form.contacts-placeholder"), - helpBlockText: $.t("client.client-form.contacts-help"), - collection: this.contactsCollection}); - $("#contacts .controls", this.el).html(contactsView.render().el); - this.listWidgetViews.push(contactsView); - - // build and bind post-logout redirect URIs - _.each(this.model.get('postLogoutRedirectUris'), function(postLogoutRedirectUri) { - _self.postLogoutRedirectUrisCollection.add(new URIModel({item:postLogoutRedirectUri})); - }); - - var postLogoutRedirectUrisView = new ListWidgetView({ - type: 'uri', - placeholder: 'https://', - helpBlockText: $.t('client.client-form.post-logout-help'), - collection: this.postLogoutRedirectUrisCollection}); - $('#postLogoutRedirectUris .controls', this.el).html(postLogoutRedirectUrisView.render().el); - this.listWidgetViews.push(postLogoutRedirectUrisView); - - // build and bind claims redirect URIs - _.each(this.model.get('claimsRedirectUris'), function(claimsRedirectUri) { - _self.claimsRedirectUrisCollection.add(new URIModel({item:claimsRedirectUri})); - }); - - var claimsRedirectUrisView = new ListWidgetView({ - type: 'uri', - placeholder: 'https://', - helpBlockText: $.t('client.client-form.claims-redirect-uris-help'), - collection: this.claimsRedirectUrisCollection}); - $('#claimsRedirectUris .controls', this.el).html(claimsRedirectUrisView.render().el); - this.listWidgetViews.push(claimsRedirectUrisView); - - // build and bind request URIs - _.each(this.model.get('requestUris'), function (requestUri) { - _self.requestUrisCollection.add(new URIModel({item:requestUri})); - }); - - var requestUriView = new ListWidgetView({ - type: 'uri', - placeholder: 'https://', - helpBlockText: $.t('client.client-form.request-uri-help'), - collection: this.requestUrisCollection}); - $('#requestUris .controls', this.el).html(requestUriView.render().el); - this.listWidgetViews.push(requestUriView); - - // build and bind default ACR values - _.each(this.model.get('defaultACRvalues'), function (defaultACRvalue) { - _self.defaultACRvaluesCollection.add(new Backbone.Model({item:defaultACRvalue})); - }); - - var defaultAcrView = new ListWidgetView({ - placeholder: $.t('client.client-form.acr-values-placeholder'), - // TODO: autocomplete from spec - helpBlockText: $.t('client.client-form.acr-values-help'), - collection: this.defaultACRvaluesCollection}); - $('#defaultAcrValues .controls', this.el).html(defaultAcrView.render().el); - this.listWidgetViews.push(defaultAcrView); - - // build and bind - - // set up token fields - if (!this.model.get("allowRefresh")) { - $("#refreshTokenValidityTime", this.$el).hide(); - } - - if (this.model.get("accessTokenValiditySeconds") == null) { - $("#access-token-timeout-time", this.$el).prop('disabled',true); - $("#access-token-timeout-unit", this.$el).prop('disabled',true); - } - - if (this.model.get("refreshTokenValiditySeconds") == null) { - $("#refresh-token-timeout-time", this.$el).prop('disabled',true); - $("#refresh-token-timeout-unit", this.$el).prop('disabled',true); - } - - // toggle other dynamic fields - this.toggleClientCredentials(); - this.previewLogo(); - this.toggleJWKSetType(); - - // disable unsupported JOSE algorithms - this.disableUnsupportedJOSEItems(app.serverConfiguration.request_object_signing_alg_values_supported, '#requestObjectSigningAlg option'); - this.disableUnsupportedJOSEItems(app.serverConfiguration.userinfo_signing_alg_values_supported, '#userInfoSignedResponseAlg option'); - this.disableUnsupportedJOSEItems(app.serverConfiguration.userinfo_encryption_alg_values_supported, '#userInfoEncryptedResponseAlg option'); - this.disableUnsupportedJOSEItems(app.serverConfiguration.userinfo_encryption_enc_values_supported, '#userInfoEncryptedResponseEnc option'); - this.disableUnsupportedJOSEItems(app.serverConfiguration.id_token_signing_alg_values_supported, '#idTokenSignedResponseAlg option'); - this.disableUnsupportedJOSEItems(app.serverConfiguration.id_token_encryption_alg_values_supported, '#idTokenEncryptedResponseAlg option'); - this.disableUnsupportedJOSEItems(app.serverConfiguration.id_token_encryption_enc_values_supported, '#idTokenEncryptedResponseEnc option'); - this.disableUnsupportedJOSEItems(app.serverConfiguration.token_endpoint_auth_signing_alg_values_supported, '#tokenEndpointAuthSigningAlg option'); - - this.$('.nyi').clickover({ - placement: 'right', - title: $.t('common.not-yet-implemented'), - content: $.t('common.not-yet-implemented-content') - }); - - $(this.el).i18n(); - return this; - } -}); + } + + // process the JWKS + var jwksUri = null; + var jwks = null; + var jwkSelector = $('#jwkSelector input:radio', this.el).filter(':checked').val(); + + if (jwkSelector == 'URI') { + jwksUri = $('#jwksUri input').val(); + jwks = null; + } else if (jwkSelector == 'VAL') { + jwksUri = null; + try { + jwks = JSON.parse($('#jwks textarea').val()); + } catch (e) { + console.log("An error occurred when parsing the JWK Set"); + app.errorHandlerView.showErrorMessage($.t("client.client-form.error.jwk-set"), $.t("client.client-form.error.jwk-set-parse")); + return false; + } + } else { + jwksUri = null; + jwks = null; + } + + var attrs = { + clientName: this.emptyToNull($('#clientName input').val()), + clientId: this.emptyToNull($('#clientId input').val()), + clientSecret: clientSecret, + generateClientSecret: generateClientSecret, + redirectUris: redirectUris, + clientDescription: this.emptyToNull($('#clientDescription textarea').val()), + logoUri: this.emptyToNull($('#logoUri input').val()), + grantTypes: grantTypes, + accessTokenValiditySeconds: accessTokenValiditySeconds, + refreshTokenValiditySeconds: refreshTokenValiditySeconds, + idTokenValiditySeconds: idTokenValiditySeconds, + deviceCodeValiditySeconds: deviceCodeValiditySeconds, + allowRefresh: $('#allowRefresh').is(':checked'), + allowIntrospection: $('#allowIntrospection input').is(':checked'), + scope: scopes, + tosUri: this.emptyToNull($('#tosUri input').val()), + policyUri: this.emptyToNull($('#policyUri input').val()), + clientUri: this.emptyToNull($('#clientUri input').val()), + applicationType: $('#applicationType input').filter(':checked').val(), + jwksUri: jwksUri, + jwks: jwks, + subjectType: subjectType, + softwareStatement: this.emptyToNull($('#softwareStatement textarea').val()), + softwareId: this.emptyToNull($('#softwareId input').val()), + softwareVersion: this.emptyToNull($('#softwareVersion input').val()), + tokenEndpointAuthMethod: tokenEndpointAuthMethod, + responseTypes: responseTypes, + sectorIdentifierUri: sectorIdentifierUri, + initiateLoginUri: this.emptyToNull($('#initiateLoginUri input').val()), + postLogoutRedirectUris: this.postLogoutRedirectUrisCollection.pluck('item'), + claimsRedirectUris: this.claimsRedirectUrisCollection.pluck('item'), + reuseRefreshToken: $('#reuseRefreshToken').is(':checked'), + clearAccessTokensOnRefresh: $('#clearAccessTokensOnRefresh').is(':checked'), + requireAuthTime: $('#requireAuthTime input').is(':checked'), + defaultMaxAge: parseInt($('#defaultMaxAge input').val()), + contacts: this.contactsCollection.pluck('item'), + requestUris: this.requestUrisCollection.pluck('item'), + defaultACRvalues: this.defaultACRvaluesCollection.pluck('item'), + requestObjectSigningAlg: this.defaultToNull($('#requestObjectSigningAlg select').val()), + userInfoSignedResponseAlg: this.defaultToNull($('#userInfoSignedResponseAlg select').val()), + userInfoEncryptedResponseAlg: this.defaultToNull($('#userInfoEncryptedResponseAlg select').val()), + userInfoEncryptedResponseEnc: this.defaultToNull($('#userInfoEncryptedResponseEnc select').val()), + idTokenSignedResponseAlg: this.defaultToNull($('#idTokenSignedResponseAlg select').val()), + idTokenEncryptedResponseAlg: this.defaultToNull($('#idTokenEncryptedResponseAlg select').val()), + idTokenEncryptedResponseEnc: this.defaultToNull($('#idTokenEncryptedResponseEnc select').val()), + tokenEndpointAuthSigningAlg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val()), + codeChallengeMethod: this.defaultToNull($('#codeChallengeMethod select').val()) + }; + + // post-validate + if (attrs["allowRefresh"] == false) { + attrs["refreshTokenValiditySeconds"] = null; + } + + if ($('#disableAccessTokenTimeout').is(':checked')) { + attrs["accessTokenValiditySeconds"] = null; + } + + if ($('#disableRefreshTokenTimeout').is(':checked')) { + attrs["refreshTokenValiditySeconds"] = null; + } + + // set all empty strings to nulls + for ( var key in attrs) { + if (attrs[key] === "") { + attrs[key] = null; + } + } + + var _self = this; + this.model.save(attrs, { + success: function() { -ui.routes.push({path: "admin/clients", name: "listClients", callback: - function () { + $('#modalAlertLabel').html($.t('client.client-form.saved.saved')); - if (!isAdmin()) { - this.root(); - return; - } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('client.manage'), href:"manage/#admin/clients"} - ]); - - this.updateSidebar('admin/clients'); + var savedModel = { + clientId: _self.model.get('clientId'), + clientSecret: _self.model.get('clientSecret'), + secretChanged: secretChanged + }; + + $('#modalAlert div.modal-header').html($.t('client.client-form.saved.saved')); + + $('#modalAlert .modal-body').html(_self.clientSavedTemplate(savedModel)); + + $('#modalAlert .modal-body #savedClientSecret').hide(); + + $('#modalAlert').on('click', '#clientSaveShow', function(event) { + event.preventDefault(); + $('#clientSaveShow').hide(); + $('#savedClientSecret').show(); + }); + + $('#modalAlert').i18n(); + $('#modalAlert').modal({ + 'backdrop': 'static', + 'keyboard': true, + 'show': true + }); + + app.clientList.add(_self.model); + app.navigate('admin/clients', { + trigger: true + }); + }, + error: app.errorHandlerView.handleError({ + log: "An error occurred when saving a client" + }) + }); + + return false; + }, - var view = new ClientListView({model:this.clientList, systemScopeList: this.systemScopeList, whiteListList: this.whiteListList}); - view.load(function() { - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('client.manage')); - }); + render: function(eventName) { - } + var data = { + client: this.model.toJSON(), + heartMode: heartMode + }; + $(this.el).html(this.template(data)); + + var _self = this; + + // clear the sub-view collection + this.listWidgetViews = []; + + // build and bind registered redirect URI collection and view + _.each(this.model.get("redirectUris"), function(redirectUri) { + _self.redirectUrisCollection.add(new URIModel({ + item: redirectUri + })); + }); + + var redirUriView = new ListWidgetView({ + type: 'uri', + placeholder: 'https://', + helpBlockText: $.t('client.client-form.redirect-uris-help'), + collection: this.redirectUrisCollection + }); + $("#redirectUris .controls", this.el).html(redirUriView.render().el); + this.listWidgetViews.push(redirUriView); + + // build and bind scopes + _.each(this.model.get("scope"), function(scope) { + _self.scopeCollection.add(new Backbone.Model({ + item: scope + })); + }); + + var scopeView = new ListWidgetView({ + placeholder: $.t('client.client-form.scope-placeholder'), + autocomplete: _.uniq(_.flatten(this.options.systemScopeList.pluck("value"))), + helpBlockText: $.t('client.client-form.scope-help'), + collection: this.scopeCollection + }); + $("#scope .controls", this.el).html(scopeView.render().el); + this.listWidgetViews.push(scopeView); + + // build and bind contacts + _.each(this.model.get('contacts'), function(contact) { + _self.contactsCollection.add(new Backbone.Model({ + item: contact + })); + }); + + var contactsView = new ListWidgetView({ + placeholder: $.t("client.client-form.contacts-placeholder"), + helpBlockText: $.t("client.client-form.contacts-help"), + collection: this.contactsCollection + }); + $("#contacts .controls", this.el).html(contactsView.render().el); + this.listWidgetViews.push(contactsView); + + // build and bind post-logout redirect URIs + _.each(this.model.get('postLogoutRedirectUris'), function(postLogoutRedirectUri) { + _self.postLogoutRedirectUrisCollection.add(new URIModel({ + item: postLogoutRedirectUri + })); + }); + + var postLogoutRedirectUrisView = new ListWidgetView({ + type: 'uri', + placeholder: 'https://', + helpBlockText: $.t('client.client-form.post-logout-help'), + collection: this.postLogoutRedirectUrisCollection + }); + $('#postLogoutRedirectUris .controls', this.el).html(postLogoutRedirectUrisView.render().el); + this.listWidgetViews.push(postLogoutRedirectUrisView); + + // build and bind claims redirect URIs + _.each(this.model.get('claimsRedirectUris'), function(claimsRedirectUri) { + _self.claimsRedirectUrisCollection.add(new URIModel({ + item: claimsRedirectUri + })); + }); + + var claimsRedirectUrisView = new ListWidgetView({ + type: 'uri', + placeholder: 'https://', + helpBlockText: $.t('client.client-form.claims-redirect-uris-help'), + collection: this.claimsRedirectUrisCollection + }); + $('#claimsRedirectUris .controls', this.el).html(claimsRedirectUrisView.render().el); + this.listWidgetViews.push(claimsRedirectUrisView); + + // build and bind request URIs + _.each(this.model.get('requestUris'), function(requestUri) { + _self.requestUrisCollection.add(new URIModel({ + item: requestUri + })); + }); + + var requestUriView = new ListWidgetView({ + type: 'uri', + placeholder: 'https://', + helpBlockText: $.t('client.client-form.request-uri-help'), + collection: this.requestUrisCollection + }); + $('#requestUris .controls', this.el).html(requestUriView.render().el); + this.listWidgetViews.push(requestUriView); + + // build and bind default ACR values + _.each(this.model.get('defaultACRvalues'), function(defaultACRvalue) { + _self.defaultACRvaluesCollection.add(new Backbone.Model({ + item: defaultACRvalue + })); + }); + + var defaultAcrView = new ListWidgetView({ + placeholder: $.t('client.client-form.acr-values-placeholder'), + // TODO: autocomplete from spec + helpBlockText: $.t('client.client-form.acr-values-help'), + collection: this.defaultACRvaluesCollection + }); + $('#defaultAcrValues .controls', this.el).html(defaultAcrView.render().el); + this.listWidgetViews.push(defaultAcrView); + + // build and bind + + // set up token fields + if (!this.model.get("allowRefresh")) { + $("#refreshTokenValidityTime", this.$el).hide(); + } + + if (this.model.get("accessTokenValiditySeconds") == null) { + $("#access-token-timeout-time", this.$el).prop('disabled', true); + $("#access-token-timeout-unit", this.$el).prop('disabled', true); + } + + if (this.model.get("refreshTokenValiditySeconds") == null) { + $("#refresh-token-timeout-time", this.$el).prop('disabled', true); + $("#refresh-token-timeout-unit", this.$el).prop('disabled', true); + } + + // toggle other dynamic fields + this.toggleClientCredentials(); + this.previewLogo(); + this.toggleJWKSetType(); + + // disable unsupported JOSE algorithms + this.disableUnsupportedJOSEItems(app.serverConfiguration.request_object_signing_alg_values_supported, '#requestObjectSigningAlg option'); + this.disableUnsupportedJOSEItems(app.serverConfiguration.userinfo_signing_alg_values_supported, '#userInfoSignedResponseAlg option'); + this.disableUnsupportedJOSEItems(app.serverConfiguration.userinfo_encryption_alg_values_supported, '#userInfoEncryptedResponseAlg option'); + this.disableUnsupportedJOSEItems(app.serverConfiguration.userinfo_encryption_enc_values_supported, '#userInfoEncryptedResponseEnc option'); + this.disableUnsupportedJOSEItems(app.serverConfiguration.id_token_signing_alg_values_supported, '#idTokenSignedResponseAlg option'); + this.disableUnsupportedJOSEItems(app.serverConfiguration.id_token_encryption_alg_values_supported, '#idTokenEncryptedResponseAlg option'); + this.disableUnsupportedJOSEItems(app.serverConfiguration.id_token_encryption_enc_values_supported, '#idTokenEncryptedResponseEnc option'); + this.disableUnsupportedJOSEItems(app.serverConfiguration.token_endpoint_auth_signing_alg_values_supported, '#tokenEndpointAuthSigningAlg option'); + + this.$('.nyi').clickover({ + placement: 'right', + title: $.t('common.not-yet-implemented'), + content: $.t('common.not-yet-implemented-content') + }); + + $(this.el).i18n(); + return this; + } +}); + +ui.routes.push({ + path: "admin/clients", + name: "listClients", + callback: function() { + + if (!isAdmin()) { + this.root(); + return; + } + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('client.manage'), + href: "manage/#admin/clients" + }]); + + this.updateSidebar('admin/clients'); + + var view = new ClientListView({ + model: this.clientList, + systemScopeList: this.systemScopeList, + whiteListList: this.whiteListList + }); + view.load(function() { + $('#content').html(view.render().el); + view.delegateEvents(); + setPageTitle($.t('client.manage')); + }); + + } }); -ui.routes.push({path: "admin/client/new", name: "newClient", callback: - function() { +ui.routes.push({ + path: "admin/client/new", + name: "newClient", + callback: function() { console.log("newClient"); if (!isAdmin()) { this.root(); return; } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('client.manage'), href:"manage/#admin/clients"}, - {text:$.t('client.client-form.new'), href:""} - ]); - - this.updateSidebar('admin/clients'); - - var client = new ClientModel(); - - var view = new ClientFormView({model:client, systemScopeList: this.systemScopeList}); - view.load(function() { + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('client.manage'), + href: "manage/#admin/clients" + }, { + text: $.t('client.client-form.new'), + href: "" + }]); + + this.updateSidebar('admin/clients'); + + var client = new ClientModel(); + + var view = new ClientFormView({ + model: client, + systemScopeList: this.systemScopeList + }); + view.load(function() { var userInfo = getUserInfo(); var contacts = []; if (userInfo != null && userInfo.email != null) { contacts.push(userInfo.email); } - + // use a different set of defaults based on heart mode flag if (heartMode) { - client.set({ - tokenEndpointAuthMethod: "PRIVATE_KEY", - generateClientSecret:false, - displayClientSecret:false, - requireAuthTime:true, - defaultMaxAge:60000, - scope: _.uniq(_.flatten(app.systemScopeList.defaultScopes().pluck("value"))), - accessTokenValiditySeconds:3600, - refreshTokenValiditySeconds:24*3600, - idTokenValiditySeconds:300, - deviceCodeValiditySeconds:30*60, - grantTypes: ["authorization_code"], - responseTypes: ["code"], - subjectType: "PUBLIC", - jwksType: "URI", - contacts: contacts - }, { silent: true }); + client.set({ + tokenEndpointAuthMethod: "PRIVATE_KEY", + generateClientSecret: false, + displayClientSecret: false, + requireAuthTime: true, + defaultMaxAge: 60000, + scope: _.uniq(_.flatten(app.systemScopeList.defaultScopes().pluck("value"))), + accessTokenValiditySeconds: 3600, + refreshTokenValiditySeconds: 24 * 3600, + idTokenValiditySeconds: 300, + deviceCodeValiditySeconds: 30 * 60, + grantTypes: ["authorization_code"], + responseTypes: ["code"], + subjectType: "PUBLIC", + jwksType: "URI", + contacts: contacts + }, { + silent: true + }); } else { - // set up this new client to require a secret and have us autogenerate one - client.set({ - tokenEndpointAuthMethod: "SECRET_BASIC", - generateClientSecret:true, - displayClientSecret:false, - requireAuthTime:true, - defaultMaxAge:60000, - scope: _.uniq(_.flatten(app.systemScopeList.defaultScopes().pluck("value"))), - accessTokenValiditySeconds:3600, - idTokenValiditySeconds:600, - deviceCodeValiditySeconds:30*60, - grantTypes: ["authorization_code"], - responseTypes: ["code"], - subjectType: "PUBLIC", - jwksType: "URI", - contacts: contacts - }, { silent: true }); + // set up this new client to require a secret and have us + // autogenerate one + client.set({ + tokenEndpointAuthMethod: "SECRET_BASIC", + generateClientSecret: true, + displayClientSecret: false, + requireAuthTime: true, + defaultMaxAge: 60000, + scope: _.uniq(_.flatten(app.systemScopeList.defaultScopes().pluck("value"))), + accessTokenValiditySeconds: 3600, + idTokenValiditySeconds: 600, + deviceCodeValiditySeconds: 30 * 60, + grantTypes: ["authorization_code"], + responseTypes: ["code"], + subjectType: "PUBLIC", + jwksType: "URI", + contacts: contacts + }, { + silent: true + }); } - - - $('#content').html(view.render().el); - setPageTitle($.t('client.client-form.new')); - }); + + $('#content').html(view.render().el); + setPageTitle($.t('client.client-form.new')); + }); } }); -ui.routes.push({path: "admin/client/:id", name: "editClient", callback: - function(id) { +ui.routes.push({ + path: "admin/client/:id", + name: "editClient", + callback: function(id) { console.log("editClient " + id); if (!isAdmin()) { this.root(); return; } - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('client.manage'), href:"manage/#admin/clients"}, - {text:$.t('client.client-form.edit'), href:"manage/#admin/client/" + id} - ]); - - this.updateSidebar('admin/clients'); - - var client = this.clientList.get(id); - if (!client) { - client = new ClientModel({id:id}); - } - - var view = new ClientFormView({model:client, systemScopeList: app.systemScopeList}); - view.load(function() { - if ($.inArray("refresh_token", client.get("grantTypes")) != -1) { - client.set({ - allowRefresh: true - }, { silent: true }); - } - - if (client.get("jwks")) { - client.set({ - jwksType: "VAL" - }, { silent: true }); - } else { - client.set({ - jwksType: "URI" - }, { silent: true }); - } - - client.set({ - generateClientSecret:false, - displayClientSecret:false - }, { silent: true }); - - $('#content').html(view.render().el); - setPageTitle($.t('client.client-form.edit')); - }); - + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('client.manage'), + href: "manage/#admin/clients" + }, { + text: $.t('client.client-form.edit'), + href: "manage/#admin/client/" + id + }]); + + this.updateSidebar('admin/clients'); + + var client = this.clientList.get(id); + if (!client) { + client = new ClientModel({ + id: id + }); + } + + var view = new ClientFormView({ + model: client, + systemScopeList: app.systemScopeList + }); + view.load(function() { + if ($.inArray("refresh_token", client.get("grantTypes")) != -1) { + client.set({ + allowRefresh: true + }, { + silent: true + }); + } + + if (client.get("jwks")) { + client.set({ + jwksType: "VAL" + }, { + silent: true + }); + } else { + client.set({ + jwksType: "URI" + }, { + silent: true + }); + } + + client.set({ + generateClientSecret: false, + displayClientSecret: false + }, { + silent: true + }); + + $('#content').html(view.render().el); + setPageTitle($.t('client.client-form.edit')); + }); + } }); ui.templates.push('resources/template/client.html'); ui.init.push(function(app) { - app.clientList = new ClientCollection(); + app.clientList = new ClientCollection(); }); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js b/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js index dca9fe2f25..81b052a916 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/dynreg.js @@ -15,713 +15,816 @@ * limitations under the License. *******************************************************************************/ var DynRegClient = Backbone.Model.extend({ - idAttribute: "client_id", - - defaults:{ - client_id:null, - client_secret:null, - redirect_uris:[], - client_name:null, - client_uri:null, - logo_uri:null, - contacts:[], - tos_uri:null, - token_endpoint_auth_method:null, - scope:null, - grant_types:[], - response_types:[], - policy_uri:null, - - jwks_uri:null, - jwks:null, - jwksType:'URI', - - application_type:null, - sector_identifier_uri:null, - subject_type:null, - - request_object_signing_alg:null, - - userinfo_signed_response_alg:null, - userinfo_encrypted_response_alg:null, - userinfo_encrypted_response_enc:null, - - id_token_signed_response_alg:null, - id_token_encrypted_response_alg:null, - id_token_encrypted_response_enc:null, - - default_max_age:null, - require_auth_time:false, - default_acr_values:null, - - initiate_login_uri:null, - post_logout_redirect_uris:null, - - claims_redirect_uris:[], - - request_uris:[], - - software_statement:null, - software_id:null, - software_version:null, - - code_challenge_method:null, - - registration_access_token:null, - registration_client_uri:null - }, - - sync: function(method, model, options){ - if (model.get('registration_access_token')) { - var headers = options.headers ? options.headers : {}; - headers['Authorization'] = 'Bearer ' + model.get('registration_access_token'); - options.headers = headers; - } - - return this.constructor.__super__.sync(method, model, options); - }, - - urlRoot:'register' - + idAttribute: "client_id", + + defaults: { + client_id: null, + client_secret: null, + redirect_uris: [], + client_name: null, + client_uri: null, + logo_uri: null, + contacts: [], + tos_uri: null, + token_endpoint_auth_method: null, + scope: null, + grant_types: [], + response_types: [], + policy_uri: null, + + jwks_uri: null, + jwks: null, + jwksType: 'URI', + + application_type: null, + sector_identifier_uri: null, + subject_type: null, + + request_object_signing_alg: null, + + userinfo_signed_response_alg: null, + userinfo_encrypted_response_alg: null, + userinfo_encrypted_response_enc: null, + + id_token_signed_response_alg: null, + id_token_encrypted_response_alg: null, + id_token_encrypted_response_enc: null, + + default_max_age: null, + require_auth_time: false, + default_acr_values: null, + + initiate_login_uri: null, + post_logout_redirect_uris: null, + + claims_redirect_uris: [], + + request_uris: [], + + software_statement: null, + software_id: null, + software_version: null, + + code_challenge_method: null, + + registration_access_token: null, + registration_client_uri: null + }, + + sync: function(method, model, options) { + if (model.get('registration_access_token')) { + var headers = options.headers ? options.headers : {}; + headers['Authorization'] = 'Bearer ' + model.get('registration_access_token'); + options.headers = headers; + } + + return this.constructor.__super__.sync(method, model, options); + }, + + urlRoot: 'register' + }); var DynRegRootView = Backbone.View.extend({ - + tagName: 'span', - - initialize:function(options) { - this.options = options; - + + initialize: function(options) { + this.options = options; + }, - - events:{ - "click #newreg":"newReg", - "click #editreg":"editReg" + + events: { + "click #newreg": "newReg", + "click #editreg": "editReg" }, - - load:function(callback) { - if (this.options.systemScopeList.isFetched) { - callback(); - return; - } - - $('#loadingbox').sheet('show'); - $('#loading').html('' + $.t('common.scopes') + ' '); - - $.when(this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}, error:app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - callback(); - }); + + load: function(callback) { + if (this.options.systemScopeList.isFetched) { + callback(); + return; + } + + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t('common.scopes') + ' '); + + $.when(this.options.systemScopeList.fetchIfNeeded({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + callback(); + }); }, - - render:function() { - $(this.el).html($('#tmpl-dynreg').html()); - $(this.el).i18n(); - return this; + + render: function() { + $(this.el).html($('#tmpl-dynreg').html()); + $(this.el).i18n(); + return this; }, - - newReg:function(e) { - e.preventDefault(); - this.remove(); - app.navigate('dev/dynreg/new', {trigger: true}); + + newReg: function(e) { + e.preventDefault(); + this.remove(); + app.navigate('dev/dynreg/new', { + trigger: true + }); }, - - editReg:function(e) { - e.preventDefault(); + + editReg: function(e) { + e.preventDefault(); var clientId = $('#clientId').val(); var token = $('#regtoken').val(); - + var client = new DynRegClient({ client_id: clientId, registration_access_token: token }); - + var self = this; - + client.fetch({ success: function() { - var userInfo = getUserInfo(); - var contacts = client.get("contacts"); - if (userInfo != null && userInfo.email != null && ! _.contains(contacts, userInfo.email)) { - contacts.push(userInfo.email); - } - client.set({ - contacts: contacts - }, { silent: true }); - - if (client.get("jwks")) { - client.set({ - jwksType: "VAL" - }, { silent: true }); - } else { - client.set({ - jwksType: "URI" - }, { silent: true }); - } - - var view = new DynRegEditView({model: client, systemScopeList: app.systemScopeList}); - - view.load(function() { - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('dynreg.edit-dynamically-registered')); - app.navigate('dev/dynreg/edit', {trigger: true}); - self.remove(); - }); - }, error:app.errorHandlerView.handleError({message: $.t('dynreg.invalid-access-token')}) + var userInfo = getUserInfo(); + var contacts = client.get("contacts"); + if (userInfo != null && userInfo.email != null && !_.contains(contacts, userInfo.email)) { + contacts.push(userInfo.email); + } + client.set({ + contacts: contacts + }, { + silent: true + }); + + if (client.get("jwks")) { + client.set({ + jwksType: "VAL" + }, { + silent: true + }); + } else { + client.set({ + jwksType: "URI" + }, { + silent: true + }); + } + + var view = new DynRegEditView({ + model: client, + systemScopeList: app.systemScopeList + }); + + view.load(function() { + $('#content').html(view.render().el); + view.delegateEvents(); + setPageTitle($.t('dynreg.edit-dynamically-registered')); + app.navigate('dev/dynreg/edit', { + trigger: true + }); + self.remove(); + }); + }, + error: app.errorHandlerView.handleError({ + message: $.t('dynreg.invalid-access-token') + }) }); } - + }); var DynRegEditView = Backbone.View.extend({ - + tagName: 'span', - - initialize:function(options) { - this.options = options; - if (!this.template) { - this.template = _.template($('#tmpl-dynreg-client-form').html()); - } - - this.redirectUrisCollection = new Backbone.Collection(); - this.scopeCollection = new Backbone.Collection(); - this.contactsCollection = new Backbone.Collection(); - this.defaultAcrValuesCollection = new Backbone.Collection(); - this.requestUrisCollection = new Backbone.Collection(); - this.postLogoutRedirectUrisCollection = new Backbone.Collection(); - this.claimsRedirectUrisCollection = new Backbone.Collection(); - - this.listWidgetViews = []; + + initialize: function(options) { + this.options = options; + if (!this.template) { + this.template = _.template($('#tmpl-dynreg-client-form').html()); + } + + this.redirectUrisCollection = new Backbone.Collection(); + this.scopeCollection = new Backbone.Collection(); + this.contactsCollection = new Backbone.Collection(); + this.defaultAcrValuesCollection = new Backbone.Collection(); + this.requestUrisCollection = new Backbone.Collection(); + this.postLogoutRedirectUrisCollection = new Backbone.Collection(); + this.claimsRedirectUrisCollection = new Backbone.Collection(); + + this.listWidgetViews = []; + }, + + load: function(callback) { + if (this.options.systemScopeList.isFetched) { + callback(); + return; + } + + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t('common.scopes') + ' '); + + $.when(this.options.systemScopeList.fetchIfNeeded({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + callback(); + }); + }, + + events: { + "click .btn-save": "saveClient", + "click .btn-cancel": "cancel", + "click .btn-delete": "deleteClient", + "change #logoUri input": "previewLogo", + "change #tokenEndpointAuthMethod input:radio": "toggleClientCredentials", + "change #jwkSelector input:radio": "toggleJWKSetType" + }, + + cancel: function(e) { + e.preventDefault(); + app.navigate('dev/dynreg', { + trigger: true + }); + }, + + deleteClient: function(e) { + e.preventDefault(); + + if (confirm($.t('client.client-table.confirm'))) { + var self = this; + + this.model.destroy({ + dataType: false, + processData: false, + success: function() { + self.remove(); + app.navigate('dev/dynreg', { + trigger: true + }); + }, + error: app.errorHandlerView.handleError({ + "log": "An error occurred when deleting a client" + }) + }); + + } + + return false; + }, + + previewLogo: function() { + if ($('#logoUri input', this.el).val()) { + $('#logoPreview', this.el).empty(); + $('#logoPreview', this.el).attr('src', $('#logoUri input', this.el).val()); + } else { + // $('#logoBlock', this.el).hide(); + $('#logoPreview', this.el).attr('src', 'resources/images/logo_placeholder.gif'); + } }, - - load:function(callback) { - if (this.options.systemScopeList.isFetched) { - callback(); - return; - } - - $('#loadingbox').sheet('show'); - $('#loading').html('' + $.t('common.scopes') + ' '); - - $.when(this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}, error:app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - callback(); - }); + + /** + * Set up the form based on the current state of the tokenEndpointAuthMethod + * parameter + * + * @param event + */ + toggleClientCredentials: function() { + + var tokenEndpointAuthMethod = $('#tokenEndpointAuthMethod input', this.el).filter(':checked').val(); + + // show or hide the signing algorithm method depending on what's + // selected + if (tokenEndpointAuthMethod == 'private_key_jwt' || tokenEndpointAuthMethod == 'client_secret_jwt') { + $('#tokenEndpointAuthSigningAlg', this.el).show(); + } else { + $('#tokenEndpointAuthSigningAlg', this.el).hide(); + } }, - - events:{ - "click .btn-save":"saveClient", - "click .btn-cancel":"cancel", - "click .btn-delete":"deleteClient", - "change #logoUri input":"previewLogo", - "change #tokenEndpointAuthMethod input:radio":"toggleClientCredentials", - "change #jwkSelector input:radio":"toggleJWKSetType" - }, - - cancel:function(e) { - e.preventDefault(); - app.navigate('dev/dynreg', {trigger: true}); - }, - - deleteClient:function (e) { - e.preventDefault(); - - if (confirm($.t('client.client-table.confirm'))) { - var self = this; - - this.model.destroy({ - dataType: false, processData: false, - success:function () { - self.remove(); - app.navigate('dev/dynreg', {trigger: true}); - }, - error:app.errorHandlerView.handleError({"log": "An error occurred when deleting a client"}) - }); - - } - - return false; - }, - - previewLogo:function() { - if ($('#logoUri input', this.el).val()) { - $('#logoPreview', this.el).empty(); - $('#logoPreview', this.el).attr('src', $('#logoUri input', this.el).val()); - } else { - //$('#logoBlock', this.el).hide(); - $('#logoPreview', this.el).attr('src', 'resources/images/logo_placeholder.gif'); - } - }, - - /** - * Set up the form based on the current state of the tokenEndpointAuthMethod parameter - * @param event - */ - toggleClientCredentials:function() { - - var tokenEndpointAuthMethod = $('#tokenEndpointAuthMethod input', this.el).filter(':checked').val(); - - // show or hide the signing algorithm method depending on what's selected - if (tokenEndpointAuthMethod == 'private_key_jwt' - || tokenEndpointAuthMethod == 'client_secret_jwt') { - $('#tokenEndpointAuthSigningAlg', this.el).show(); - } else { - $('#tokenEndpointAuthSigningAlg', this.el).hide(); - } - }, - - /** - * Set up the form based on the JWK Set selector - */ - toggleJWKSetType:function() { - var jwkSelector = $('#jwkSelector input:radio', this.el).filter(':checked').val(); - - if (jwkSelector == 'URI') { - $('#jwksUri', this.el).show(); - $('#jwks', this.el).hide(); - } else if (jwkSelector == 'VAL') { - $('#jwksUri', this.el).hide(); - $('#jwks', this.el).show(); - } else { - $('#jwksUri', this.el).hide(); - $('#jwks', this.el).hide(); - } - - }, - - disableUnsupportedJOSEItems:function(serverSupported, query) { - var supported = ['default']; - if (serverSupported) { - supported = _.union(supported, serverSupported); - } - $(query, this.$el).each(function(idx) { - if(_.contains(supported, $(this).val())) { - $(this).prop('disabled', false); - } else { - $(this).prop('disabled', true); - } - }); - - }, - - // returns "null" if given the value "default" as a string, otherwise returns input value. useful for parsing the JOSE algorithm dropdowns - defaultToNull:function(value) { - if (value == 'default') { - return null; - } else { - return value; - } - }, - - // returns "null" if the given value is falsy - emptyToNull:function(value) { - if (value) { - return value; - } else { - return null; - } - }, - - // maps from a form-friendly name to the real grant parameter name - grantMap:{ - 'authorization_code': 'authorization_code', - 'password': 'password', - 'implicit': 'implicit', - 'client_credentials': 'client_credentials', - 'redelegate': 'urn:ietf:params:oauth:grant_type:redelegate', - 'refresh_token': 'refresh_token' - }, - - // maps from a form-friendly name to the real response type parameter name - responseMap:{ - 'code': 'code', - 'token': 'token', - 'idtoken': 'id_token', - 'token-idtoken': 'token id_token', - 'code-idtoken': 'code id_token', - 'code-token': 'code token', - 'code-token-idtoken': 'code token id_token' - }, - - saveClient:function (e) { - e.preventDefault(); - - $('.control-group').removeClass('error'); - - // sync any leftover collection items - _.each(this.listWidgetViews, function(v) { - v.addItem($.Event('click')); - }); - - // build the scope object - var scopes = this.scopeCollection.pluck("item").join(" "); - - // build the grant type object - var grantTypes = []; - $.each(this.grantMap, function(index,type) { - if ($('#grantTypes-' + index).is(':checked')) { - grantTypes.push(type); - } - }); - - // build the response type object - var responseTypes = []; - $.each(this.responseMap, function(index,type) { - if ($('#responseTypes-' + index).is(':checked')) { - responseTypes.push(type); - } - }); - - var contacts = this.contactsCollection.pluck('item'); - var userInfo = getUserInfo(); - if (userInfo && userInfo.email) { - if (!_.contains(contacts, userInfo.email)) { - contacts.push(userInfo.email); - } - } - - // make sure that the subject identifier is consistent with the redirect URIs - var subjectType = $('#subjectType input').filter(':checked').val(); - var redirectUris = this.redirectUrisCollection.pluck("item"); - var sectorIdentifierUri = $('#sectorIdentifierUri input').val(); - if (subjectType == 'PAIRWISE' && redirectUris.length > 1 && sectorIdentifierUri == '') { - //Display an alert with an error message - app.errorHandlerView.showErrorMessage($.t("client.client-form.error.consistency"), $.t("client.client-form.error.pairwise-sector")); + + /** + * Set up the form based on the JWK Set selector + */ + toggleJWKSetType: function() { + var jwkSelector = $('#jwkSelector input:radio', this.el).filter(':checked').val(); + + if (jwkSelector == 'URI') { + $('#jwksUri', this.el).show(); + $('#jwks', this.el).hide(); + } else if (jwkSelector == 'VAL') { + $('#jwksUri', this.el).hide(); + $('#jwks', this.el).show(); + } else { + $('#jwksUri', this.el).hide(); + $('#jwks', this.el).hide(); + } + + }, + + disableUnsupportedJOSEItems: function(serverSupported, query) { + var supported = ['default']; + if (serverSupported) { + supported = _.union(supported, serverSupported); + } + $(query, this.$el).each(function(idx) { + if (_.contains(supported, $(this).val())) { + $(this).prop('disabled', false); + } else { + $(this).prop('disabled', true); + } + }); + + }, + + // returns "null" if given the value "default" as a string, + // otherwise returns input value. useful for parsing the JOSE + // algorithm dropdowns + defaultToNull: function(value) { + if (value == 'default') { + return null; + } else { + return value; + } + }, + + // returns "null" if the given value is falsy + emptyToNull: function(value) { + if (value) { + return value; + } else { + return null; + } + }, + + // maps from a form-friendly name to the real grant parameter name + grantMap: { + 'authorization_code': 'authorization_code', + 'password': 'password', + 'implicit': 'implicit', + 'client_credentials': 'client_credentials', + 'redelegate': 'urn:ietf:params:oauth:grant_type:redelegate', + 'refresh_token': 'refresh_token' + }, + + // maps from a form-friendly name to the real response type + // parameter name + responseMap: { + 'code': 'code', + 'token': 'token', + 'idtoken': 'id_token', + 'token-idtoken': 'token id_token', + 'code-idtoken': 'code id_token', + 'code-token': 'code token', + 'code-token-idtoken': 'code token id_token' + }, + + saveClient: function(e) { + e.preventDefault(); + + $('.control-group').removeClass('error'); + + // sync any leftover collection items + _.each(this.listWidgetViews, function(v) { + v.addItem($.Event('click')); + }); + + // build the scope object + var scopes = this.scopeCollection.pluck("item").join(" "); + + // build the grant type object + var grantTypes = []; + $.each(this.grantMap, function(index, type) { + if ($('#grantTypes-' + index).is(':checked')) { + grantTypes.push(type); + } + }); + + // build the response type object + var responseTypes = []; + $.each(this.responseMap, function(index, type) { + if ($('#responseTypes-' + index).is(':checked')) { + responseTypes.push(type); + } + }); + + var contacts = this.contactsCollection.pluck('item'); + var userInfo = getUserInfo(); + if (userInfo && userInfo.email) { + if (!_.contains(contacts, userInfo.email)) { + contacts.push(userInfo.email); + } + } + + // make sure that the subject identifier is consistent with the + // redirect URIs + var subjectType = $('#subjectType input').filter(':checked').val(); + var redirectUris = this.redirectUrisCollection.pluck("item"); + var sectorIdentifierUri = $('#sectorIdentifierUri input').val(); + if (subjectType == 'PAIRWISE' && redirectUris.length > 1 && sectorIdentifierUri == '') { + // Display an alert with an error message + app.errorHandlerView.showErrorMessage($.t("client.client-form.error.consistency"), $.t("client.client-form.error.pairwise-sector")); return false; - - } - - // process the JWKS - var jwksUri = null; - var jwks = null; - var jwkSelector = $('#jwkSelector input:radio', this.el).filter(':checked').val(); - - if (jwkSelector == 'URI') { - jwksUri = $('#jwksUri input').val(); - jwks = null; - } else if (jwkSelector == 'VAL') { - jwksUri = null; - try { - jwks = JSON.parse($('#jwks textarea').val()); - } catch (e) { - console.log("An error occurred when parsing the JWK Set"); - app.errorHandlerView.showErrorMessage($.t("client.client-form.error.jwk-set"), $.t("client.client-form.error.jwk-set-parse")); - return false; - } - } else { - jwksUri = null; - jwks = null; - } - - var attrs = { - client_name:this.emptyToNull($('#clientName input').val()), - redirect_uris: redirectUris, - logo_uri:this.emptyToNull($('#logoUri input').val()), - grant_types: grantTypes, - scope: scopes, - client_secret: null, // never send a client secret - tos_uri: this.emptyToNull($('#tosUri input').val()), - policy_uri: this.emptyToNull($('#policyUri input').val()), - client_uri: this.emptyToNull($('#clientUri input').val()), - application_type: $('#applicationType input').filter(':checked').val(), - jwks_uri: jwksUri, - jwks: jwks, - subject_type: subjectType, - software_statement: this.emptyToNull($('#softwareStatement textarea').val()), - softwareId: this.emptyToNull($('#softwareId input').val()), - softwareVersion: this.emptyToNull($('#softwareVersion input').val()), - token_endpoint_auth_method: $('#tokenEndpointAuthMethod input').filter(':checked').val(), - response_types: responseTypes, - sector_identifier_uri: sectorIdentifierUri, - initiate_login_uri: this.emptyToNull($('#initiateLoginUri input').val()), - post_logout_redirect_uris: this.postLogoutRedirectUrisCollection.pluck('item'), - claims_redirect_uris: this.claimsRedirectUrisCollection.pluck('item'), - require_auth_time: $('#requireAuthTime input').is(':checked'), - default_max_age: parseInt($('#defaultMaxAge input').val()), - contacts: contacts, - request_uris: this.requestUrisCollection.pluck('item'), - default_acr_values: this.defaultAcrValuesCollection.pluck('item'), - request_object_signing_alg: this.defaultToNull($('#requestObjectSigningAlg select').val()), - userinfo_signed_response_alg: this.defaultToNull($('#userInfoSignedResponseAlg select').val()), - userinfo_encrypted_response_alg: this.defaultToNull($('#userInfoEncryptedResponseAlg select').val()), - userinfo_encrypted_response_enc: this.defaultToNull($('#userInfoEncryptedResponseEnc select').val()), - id_token_signed_response_alg: this.defaultToNull($('#idTokenSignedResponseAlg select').val()), - id_token_encrypted_response_alg: this.defaultToNull($('#idTokenEncryptedResponseAlg select').val()), - id_token_encrypted_response_enc: this.defaultToNull($('#idTokenEncryptedResponseEnc select').val()), - token_endpoint_auth_signing_alg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val()), - code_challenge_method: this.defaultToNull($('#codeChallengeMethod select').val()) - }; - - // set all empty strings to nulls - for (var key in attrs) { - if (attrs[key] === "") { - attrs[key] = null; - } - } - - var _self = this; - this.model.save(attrs, { - success:function () { - // switch to an "edit" view - app.navigate('dev/dynreg/edit', {trigger: true}); - _self.remove(); - - var userInfo = getUserInfo(); - var contacts = _self.model.get("contacts"); - if (userInfo != null && userInfo.email != null && ! _.contains(contacts, userInfo.email)) { - contacts.push(userInfo.email); - } - _self.model.set({ - contacts: contacts - }, { silent: true }); - - if (_self.model.get("jwks")) { - _self.model.set({ - jwksType: "VAL" - }, { silent: true }); - } else { - _self.model.set({ - jwksType: "URI" - }, { silent: true }); - } - - var view = new DynRegEditView({model: _self.model, systemScopeList: _self.options.systemScopeList}); - - view.load(function() { - // reload - $('#content').html(view.render().el); - view.delegateEvents(); - }); - }, - error:app.errorHandlerView.handleError({log: "An error occurred when saving a client"}) - }); - - return false; - }, - - render:function() { - var data = {client: this.model.toJSON(), userInfo: getUserInfo(), heartMode: heartMode}; + + } + + // process the JWKS + var jwksUri = null; + var jwks = null; + var jwkSelector = $('#jwkSelector input:radio', this.el).filter(':checked').val(); + + if (jwkSelector == 'URI') { + jwksUri = $('#jwksUri input').val(); + jwks = null; + } else if (jwkSelector == 'VAL') { + jwksUri = null; + try { + jwks = JSON.parse($('#jwks textarea').val()); + } catch (e) { + console.log("An error occurred when parsing the JWK Set"); + app.errorHandlerView.showErrorMessage($.t("client.client-form.error.jwk-set"), $.t("client.client-form.error.jwk-set-parse")); + return false; + } + } else { + jwksUri = null; + jwks = null; + } + + var attrs = { + client_name: this.emptyToNull($('#clientName input').val()), + redirect_uris: redirectUris, + logo_uri: this.emptyToNull($('#logoUri input').val()), + grant_types: grantTypes, + scope: scopes, + client_secret: null, // never send a client secret + tos_uri: this.emptyToNull($('#tosUri input').val()), + policy_uri: this.emptyToNull($('#policyUri input').val()), + client_uri: this.emptyToNull($('#clientUri input').val()), + application_type: $('#applicationType input').filter(':checked').val(), + jwks_uri: jwksUri, + jwks: jwks, + subject_type: subjectType, + software_statement: this.emptyToNull($('#softwareStatement textarea').val()), + softwareId: this.emptyToNull($('#softwareId input').val()), + softwareVersion: this.emptyToNull($('#softwareVersion input').val()), + token_endpoint_auth_method: $('#tokenEndpointAuthMethod input').filter(':checked').val(), + response_types: responseTypes, + sector_identifier_uri: sectorIdentifierUri, + initiate_login_uri: this.emptyToNull($('#initiateLoginUri input').val()), + post_logout_redirect_uris: this.postLogoutRedirectUrisCollection.pluck('item'), + claims_redirect_uris: this.claimsRedirectUrisCollection.pluck('item'), + require_auth_time: $('#requireAuthTime input').is(':checked'), + default_max_age: parseInt($('#defaultMaxAge input').val()), + contacts: contacts, + request_uris: this.requestUrisCollection.pluck('item'), + default_acr_values: this.defaultAcrValuesCollection.pluck('item'), + request_object_signing_alg: this.defaultToNull($('#requestObjectSigningAlg select').val()), + userinfo_signed_response_alg: this.defaultToNull($('#userInfoSignedResponseAlg select').val()), + userinfo_encrypted_response_alg: this.defaultToNull($('#userInfoEncryptedResponseAlg select').val()), + userinfo_encrypted_response_enc: this.defaultToNull($('#userInfoEncryptedResponseEnc select').val()), + id_token_signed_response_alg: this.defaultToNull($('#idTokenSignedResponseAlg select').val()), + id_token_encrypted_response_alg: this.defaultToNull($('#idTokenEncryptedResponseAlg select').val()), + id_token_encrypted_response_enc: this.defaultToNull($('#idTokenEncryptedResponseEnc select').val()), + token_endpoint_auth_signing_alg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val()), + code_challenge_method: this.defaultToNull($('#codeChallengeMethod select').val()) + }; + + // set all empty strings to nulls + for ( var key in attrs) { + if (attrs[key] === "") { + attrs[key] = null; + } + } + + var _self = this; + this.model.save(attrs, { + success: function() { + // switch to an "edit" view + app.navigate('dev/dynreg/edit', { + trigger: true + }); + _self.remove(); + + var userInfo = getUserInfo(); + var contacts = _self.model.get("contacts"); + if (userInfo != null && userInfo.email != null && !_.contains(contacts, userInfo.email)) { + contacts.push(userInfo.email); + } + _self.model.set({ + contacts: contacts + }, { + silent: true + }); + + if (_self.model.get("jwks")) { + _self.model.set({ + jwksType: "VAL" + }, { + silent: true + }); + } else { + _self.model.set({ + jwksType: "URI" + }, { + silent: true + }); + } + + var view = new DynRegEditView({ + model: _self.model, + systemScopeList: _self.options.systemScopeList + }); + + view.load(function() { + // reload + $('#content').html(view.render().el); + view.delegateEvents(); + }); + }, + error: app.errorHandlerView.handleError({ + log: "An error occurred when saving a client" + }) + }); + + return false; + }, + + render: function() { + var data = { + client: this.model.toJSON(), + userInfo: getUserInfo(), + heartMode: heartMode + }; $(this.el).html(this.template(data)); - + this.listWidgetViews = []; - + var _self = this; - // build and bind registered redirect URI collection and view - _.each(this.model.get("redirect_uris"), function (redirectUri) { - _self.redirectUrisCollection.add(new URIModel({item:redirectUri})); - }); - - var redirectUriView = new ListWidgetView({ - type:'uri', - placeholder: 'https://', - helpBlockText: $.t('client.client-form.redirect-uris-help'), - collection: this.redirectUrisCollection}); - $("#redirectUris .controls",this.el).html(redirectUriView.render().el); - this.listWidgetViews.push(redirectUriView); - - // build and bind scopes - var scopes = this.model.get("scope"); - var scopeSet = scopes ? scopes.split(" ") : []; - _.each(scopeSet, function (scope) { - _self.scopeCollection.add(new Backbone.Model({item:scope})); - }); - - var scopeView = new ListWidgetView({ - placeholder: $.t('client.client-form.scope-placeholder'), - autocomplete: _.uniq(_.flatten(this.options.systemScopeList.unrestrictedScopes().pluck("value"))), - helpBlockText: $.t('client.client-form.scope-help'), - collection: this.scopeCollection}); - $("#scope .controls",this.el).html(scopeView.render().el); - this.listWidgetViews.push(scopeView); - - // build and bind contacts - _.each(this.model.get('contacts'), function (contact) { - _self.contactsCollection.add(new Backbone.Model({item:contact})); - }); - - var contactView = new ListWidgetView({ - placeholder: $.t('client.client-form.contacts-placeholder'), - helpBlockText: $.t('client.client-form.contacts-help'), - collection: this.contactsCollection}); - $("#contacts .controls div", this.el).html(contactView.render().el); - this.listWidgetViews.push(contactView); - - // build and bind post-logout redirect URIs - _.each(this.model.get('post_logout_redirect_uris'), function(postLogoutRedirectUri) { - _self.postLogoutRedirectUrisCollection.add(new URIModel({item:postLogoutRedirectUri})); - }); - - var postLogoutRedirectUrisView = new ListWidgetView({ - type: 'uri', - placeholder: 'https://', - helpBlockText: $.t('client.client-form.post-logout-help'), - collection: this.postLogoutRedirectUrisCollection}); - $('#postLogoutRedirectUris .controls', this.el).html(postLogoutRedirectUrisView.render().el); - this.listWidgetViews.push(postLogoutRedirectUrisView); - - // build and bind claims redirect URIs - _.each(this.model.get('claimsRedirectUris'), function(claimsRedirectUri) { - _self.claimsRedirectUrisCollection.add(new URIModel({item:claimsRedirectUri})); - }); - - var claimsRedirectUrisView = new ListWidgetView({ - type: 'uri', - placeholder: 'https://', - helpBlockText: $.t('client.client-form.claims-redirect-uris-help'), - collection: this.claimsRedirectUrisCollection}); - $('#claimsRedirectUris .controls', this.el).html(claimsRedirectUrisView.render().el); - this.listWidgetViews.push(claimsRedirectUrisView); - - // build and bind request URIs - _.each(this.model.get('request_uris'), function (requestUri) { - _self.requestUrisCollection.add(new URIModel({item:requestUri})); - }); - - var requestUriView = new ListWidgetView({ - type: 'uri', - placeholder: 'https://', - helpBlockText: $.t('client.client-form.request-uri-help'), - collection: this.requestUrisCollection}); - $('#requestUris .controls', this.el).html(requestUriView.render().el); - this.listWidgetViews.push(requestUriView); - - // build and bind default ACR values - _.each(this.model.get('default_acr_values'), function (defaultAcrValue) { - _self.defaultAcrValuesCollection.add(new Backbone.Model({item:defaultAcrValue})); - }); - - var defaultAcrView = new ListWidgetView({ - placeholder: $.t('client.client-form.acr-values-placeholder'), - // TODO: autocomplete from spec - helpBlockText: $.t('client.client-form.acr-values-help'), - collection: this.defaultAcrValuesCollection}); - $('#defaultAcrValues .controls', this.el).html(defaultAcrView.render().el); - this.listWidgetViews.push(defaultAcrView); - - this.toggleClientCredentials(); - this.previewLogo(); - this.toggleJWKSetType(); - - // disable unsupported JOSE algorithms - this.disableUnsupportedJOSEItems(app.serverConfiguration.request_object_signing_alg_values_supported, '#requestObjectSigningAlg option'); - this.disableUnsupportedJOSEItems(app.serverConfiguration.userinfo_signing_alg_values_supported, '#userInfoSignedResponseAlg option'); - this.disableUnsupportedJOSEItems(app.serverConfiguration.userinfo_encryption_alg_values_supported, '#userInfoEncryptedResponseAlg option'); - this.disableUnsupportedJOSEItems(app.serverConfiguration.userinfo_encryption_enc_values_supported, '#userInfoEncryptedResponseEnc option'); - this.disableUnsupportedJOSEItems(app.serverConfiguration.id_token_signing_alg_values_supported, '#idTokenSignedResponseAlg option'); - this.disableUnsupportedJOSEItems(app.serverConfiguration.id_token_encryption_alg_values_supported, '#idTokenEncryptedResponseAlg option'); - this.disableUnsupportedJOSEItems(app.serverConfiguration.id_token_encryption_enc_values_supported, '#idTokenEncryptedResponseEnc option'); - this.disableUnsupportedJOSEItems(app.serverConfiguration.token_endpoint_auth_signing_alg_values_supported, '#tokenEndpointAuthSigningAlg option'); - - this.$('.nyi').clickover({ - placement: 'right', - title: $.t('common.not-yet-implemented'), - content: $.t('common.not-yet-implemented-content') - }); - - - $(this.el).i18n(); - return this; + // build and bind registered redirect URI collection and view + _.each(this.model.get("redirect_uris"), function(redirectUri) { + _self.redirectUrisCollection.add(new URIModel({ + item: redirectUri + })); + }); + + var redirectUriView = new ListWidgetView({ + type: 'uri', + placeholder: 'https://', + helpBlockText: $.t('client.client-form.redirect-uris-help'), + collection: this.redirectUrisCollection + }); + $("#redirectUris .controls", this.el).html(redirectUriView.render().el); + this.listWidgetViews.push(redirectUriView); + + // build and bind scopes + var scopes = this.model.get("scope"); + var scopeSet = scopes ? scopes.split(" ") : []; + _.each(scopeSet, function(scope) { + _self.scopeCollection.add(new Backbone.Model({ + item: scope + })); + }); + + var scopeView = new ListWidgetView({ + placeholder: $.t('client.client-form.scope-placeholder'), + autocomplete: _.uniq(_.flatten(this.options.systemScopeList.unrestrictedScopes().pluck("value"))), + helpBlockText: $.t('client.client-form.scope-help'), + collection: this.scopeCollection + }); + $("#scope .controls", this.el).html(scopeView.render().el); + this.listWidgetViews.push(scopeView); + + // build and bind contacts + _.each(this.model.get('contacts'), function(contact) { + _self.contactsCollection.add(new Backbone.Model({ + item: contact + })); + }); + + var contactView = new ListWidgetView({ + placeholder: $.t('client.client-form.contacts-placeholder'), + helpBlockText: $.t('client.client-form.contacts-help'), + collection: this.contactsCollection + }); + $("#contacts .controls div", this.el).html(contactView.render().el); + this.listWidgetViews.push(contactView); + + // build and bind post-logout redirect URIs + _.each(this.model.get('post_logout_redirect_uris'), function(postLogoutRedirectUri) { + _self.postLogoutRedirectUrisCollection.add(new URIModel({ + item: postLogoutRedirectUri + })); + }); + + var postLogoutRedirectUrisView = new ListWidgetView({ + type: 'uri', + placeholder: 'https://', + helpBlockText: $.t('client.client-form.post-logout-help'), + collection: this.postLogoutRedirectUrisCollection + }); + $('#postLogoutRedirectUris .controls', this.el).html(postLogoutRedirectUrisView.render().el); + this.listWidgetViews.push(postLogoutRedirectUrisView); + + // build and bind claims redirect URIs + _.each(this.model.get('claimsRedirectUris'), function(claimsRedirectUri) { + _self.claimsRedirectUrisCollection.add(new URIModel({ + item: claimsRedirectUri + })); + }); + + var claimsRedirectUrisView = new ListWidgetView({ + type: 'uri', + placeholder: 'https://', + helpBlockText: $.t('client.client-form.claims-redirect-uris-help'), + collection: this.claimsRedirectUrisCollection + }); + $('#claimsRedirectUris .controls', this.el).html(claimsRedirectUrisView.render().el); + this.listWidgetViews.push(claimsRedirectUrisView); + + // build and bind request URIs + _.each(this.model.get('request_uris'), function(requestUri) { + _self.requestUrisCollection.add(new URIModel({ + item: requestUri + })); + }); + + var requestUriView = new ListWidgetView({ + type: 'uri', + placeholder: 'https://', + helpBlockText: $.t('client.client-form.request-uri-help'), + collection: this.requestUrisCollection + }); + $('#requestUris .controls', this.el).html(requestUriView.render().el); + this.listWidgetViews.push(requestUriView); + + // build and bind default ACR values + _.each(this.model.get('default_acr_values'), function(defaultAcrValue) { + _self.defaultAcrValuesCollection.add(new Backbone.Model({ + item: defaultAcrValue + })); + }); + + var defaultAcrView = new ListWidgetView({ + placeholder: $.t('client.client-form.acr-values-placeholder'), + // TODO: autocomplete from spec + helpBlockText: $.t('client.client-form.acr-values-help'), + collection: this.defaultAcrValuesCollection + }); + $('#defaultAcrValues .controls', this.el).html(defaultAcrView.render().el); + this.listWidgetViews.push(defaultAcrView); + + this.toggleClientCredentials(); + this.previewLogo(); + this.toggleJWKSetType(); + + // disable unsupported JOSE algorithms + this.disableUnsupportedJOSEItems(app.serverConfiguration.request_object_signing_alg_values_supported, '#requestObjectSigningAlg option'); + this.disableUnsupportedJOSEItems(app.serverConfiguration.userinfo_signing_alg_values_supported, '#userInfoSignedResponseAlg option'); + this.disableUnsupportedJOSEItems(app.serverConfiguration.userinfo_encryption_alg_values_supported, '#userInfoEncryptedResponseAlg option'); + this.disableUnsupportedJOSEItems(app.serverConfiguration.userinfo_encryption_enc_values_supported, '#userInfoEncryptedResponseEnc option'); + this.disableUnsupportedJOSEItems(app.serverConfiguration.id_token_signing_alg_values_supported, '#idTokenSignedResponseAlg option'); + this.disableUnsupportedJOSEItems(app.serverConfiguration.id_token_encryption_alg_values_supported, '#idTokenEncryptedResponseAlg option'); + this.disableUnsupportedJOSEItems(app.serverConfiguration.id_token_encryption_enc_values_supported, '#idTokenEncryptedResponseEnc option'); + this.disableUnsupportedJOSEItems(app.serverConfiguration.token_endpoint_auth_signing_alg_values_supported, '#tokenEndpointAuthSigningAlg option'); + + this.$('.nyi').clickover({ + placement: 'right', + title: $.t('common.not-yet-implemented'), + content: $.t('common.not-yet-implemented-content') + }); + + $(this.el).i18n(); + return this; } - + }); -ui.routes.push({path: "dev/dynreg", name: "dynReg", callback: - function() { - +ui.routes.push({ + path: "dev/dynreg", + name: "dynReg", + callback: function() { + this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-client'), href:"manage/#dev/dynreg"} - ]); - - var view = new DynRegRootView({systemScopeList: this.systemScopeList}); - - this.updateSidebar('dev/dynreg'); - + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('admin.self-service-client'), + href: "manage/#dev/dynreg" + }]); + + var view = new DynRegRootView({ + systemScopeList: this.systemScopeList + }); + + this.updateSidebar('dev/dynreg'); + view.load(function() { - $('#content').html(view.render().el); - - setPageTitle($.t('admin.self-service-client')); + $('#content').html(view.render().el); + + setPageTitle($.t('admin.self-service-client')); }); - + } }); -ui.routes.push({path: "dev/dynreg/new", name: "newDynReg", callback: - function() { +ui.routes.push({ + path: "dev/dynreg/new", + name: "newDynReg", + callback: function() { this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-client'), href:"manage/#dev/dynreg"}, - {text:$.t('dynreg.new-client'), href:"manage/#dev/dynreg/new"} - ]); - - this.updateSidebar('dev/dynreg'); - + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('admin.self-service-client'), + href: "manage/#dev/dynreg" + }, { + text: $.t('dynreg.new-client'), + href: "manage/#dev/dynreg/new" + }]); + + this.updateSidebar('dev/dynreg'); + var client = new DynRegClient(); - var view = new DynRegEditView({model: client, systemScopeList:this.systemScopeList}); - + var view = new DynRegEditView({ + model: client, + systemScopeList: this.systemScopeList + }); + view.load(function() { - + var userInfo = getUserInfo(); var contacts = []; if (userInfo != null && userInfo.email != null) { contacts.push(userInfo.email); } - + if (heartMode) { client.set({ - require_auth_time:true, - default_max_age:60000, + require_auth_time: true, + default_max_age: 60000, scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), token_endpoint_auth_method: 'private_key_jwt', grant_types: ["authorization_code"], response_types: ["code"], subject_type: "public", contacts: contacts - }, { silent: true }); + }, { + silent: true + }); } else { client.set({ - require_auth_time:true, - default_max_age:60000, + require_auth_time: true, + default_max_age: 60000, scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), token_endpoint_auth_method: 'client_secret_basic', grant_types: ["authorization_code"], response_types: ["code"], subject_type: "public", contacts: contacts - }, { silent: true }); + }, { + silent: true + }); } - + $('#content').html(view.render().el); view.delegateEvents(); setPageTitle($.t('dynreg.new-client')); - + }); - + } }); -ui.routes.push({path: "dev/dynreg/edit", name: "editDynReg", callback: - function() { +ui.routes.push({ + path: "dev/dynreg/edit", + name: "editDynReg", + callback: function() { this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-client'), href:"manage/#dev/dynreg"}, - {text:$.t('dynreg.edit-existing'), href:"manage/#dev/dynreg/edit"} - ]); - - this.updateSidebar('dev/dynreg'); - + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('admin.self-service-client'), + href: "manage/#dev/dynreg" + }, { + text: $.t('dynreg.edit-existing'), + href: "manage/#dev/dynreg/edit" + }]); + + this.updateSidebar('dev/dynreg'); + setPageTitle($.t('dynreg.edit-existing')); - // note that this doesn't actually load the client, that's supposed to happen elsewhere... + // note that this doesn't actually load the client, that's supposed to + // happen elsewhere... } }); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js b/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js index 948e1c17aa..c547b1efd0 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/grant.js @@ -16,84 +16,95 @@ *******************************************************************************/ var ApprovedSiteModel = Backbone.Model.extend({ idAttribute: 'id', - - initialize: function() { }, - + + initialize: function() { + }, + urlRoot: 'api/approved' - + }); var ApprovedSiteCollection = Backbone.Collection.extend({ - initialize: function() { }, + initialize: function() { + }, model: ApprovedSiteModel, url: 'api/approved' }); - var ApprovedSiteListView = Backbone.View.extend({ tagName: 'span', - - initialize:function(options) { + + initialize: function(options) { this.options = options; }, - load:function(callback) { - if (this.model.isFetched && - this.options.clientList.isFetched && - this.options.systemScopeList.isFetched) { - callback(); - return; - } - - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t('grant.grant-table.approved-sites') + ' ' + - '' + $.t('common.clients') + ' ' + - '' + $.t('common.scopes') + ' ' - ); - - $.when(this.model.fetchIfNeeded({success:function(e) {$('#loading-grants').addClass('label-success');}, error:app.errorHandlerView.handleError()}), - this.options.clientList.fetchIfNeeded({success:function(e) {$('#loading-clients').addClass('label-success');}, error:app.errorHandlerView.handleError()}), - this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}, error:app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - callback(); - }); - }, - + load: function(callback) { + if (this.model.isFetched && this.options.clientList.isFetched && this.options.systemScopeList.isFetched) { + callback(); + return; + } + + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t('grant.grant-table.approved-sites') + ' ' + '' + $.t('common.clients') + ' ' + '' + $.t('common.scopes') + ' '); + + $.when(this.model.fetchIfNeeded({ + success: function(e) { + $('#loading-grants').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.clientList.fetchIfNeeded({ + success: function(e) { + $('#loading-clients').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.systemScopeList.fetchIfNeeded({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + callback(); + }); + }, + events: { - "click .refresh-table":"refreshTable" + "click .refresh-table": "refreshTable" }, - - render:function (eventName) { + + render: function(eventName) { $(this.el).html($('#tmpl-grant-table').html()); - + var approvedSiteCount = 0; - + var _self = this; - + _.each(this.model.models, function(approvedSite) { // look up client var client = this.options.clientList.getByClientId(approvedSite.get('clientId')); - + if (client != null) { - - var view = new ApprovedSiteView({model: approvedSite, client: client, systemScopeList: this.options.systemScopeList}); + + var view = new ApprovedSiteView({ + model: approvedSite, + client: client, + systemScopeList: this.options.systemScopeList + }); view.parentView = _self; $('#grant-table', this.el).append(view.render().el); approvedSiteCount = approvedSiteCount + 1; - + } - + }, this); - + this.togglePlaceholder(); $(this.el).i18n(); return this; }, - - togglePlaceholder:function() { + + togglePlaceholder: function() { // count entries if (this.model.length > 0) { $('#grant-table', this.el).show(); @@ -102,54 +113,62 @@ var ApprovedSiteListView = Backbone.View.extend({ $('#grant-table', this.el).hide(); $('#grant-table-empty', this.el).show(); } - + }, - - refreshTable:function(e) { - e.preventDefault(); - var _self = this; - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t('grant.grant-table.approved-sites') + ' ' + - '' + $.t('common.clients') + ' ' + - '' + $.t('common.scopes') + ' ' - ); - - $.when(this.model.fetch({success:function(e) {$('#loading-grants').addClass('label-success');}, error:app.errorHandlerView.handleError()}), - this.options.clientList.fetch({success:function(e) {$('#loading-clients').addClass('label-success');}, error:app.errorHandlerView.handleError()}), - this.options.systemScopeList.fetch({success:function(e) {$('#loading-scopes').addClass('label-success');}, error:app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - _self.render(); - }); - } + + refreshTable: function(e) { + e.preventDefault(); + var _self = this; + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t('grant.grant-table.approved-sites') + ' ' + '' + $.t('common.clients') + ' ' + '' + $.t('common.scopes') + ' '); + + $.when(this.model.fetch({ + success: function(e) { + $('#loading-grants').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.clientList.fetch({ + success: function(e) { + $('#loading-clients').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.systemScopeList.fetch({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + _self.render(); + }); + } }); var ApprovedSiteView = Backbone.View.extend({ tagName: 'tr', - + initialize: function(options) { - this.options = options; + this.options = options; if (!this.template) { this.template = _.template($('#tmpl-grant').html()); } - if (!this.scopeTemplate) { - this.scopeTemplate = _.template($('#tmpl-scope-list').html()); - } + if (!this.scopeTemplate) { + this.scopeTemplate = _.template($('#tmpl-scope-list').html()); + } - if (!this.moreInfoTemplate) { - this.moreInfoTemplate = _.template($('#tmpl-client-more-info-block').html()); - } + if (!this.moreInfoTemplate) { + this.moreInfoTemplate = _.template($('#tmpl-client-more-info-block').html()); + } }, - render: function() { - + render: function() { + var creationDate = this.model.get("creationDate"); var accessDate = this.model.get("accessDate"); var timeoutDate = this.model.get("timeoutDate"); - + var displayCreationDate = $.t('grant.grant-table.unknown'); var hoverCreationDate = ""; if ((creationDate != null) && moment(creationDate).isValid()) { @@ -178,7 +197,7 @@ var ApprovedSiteView = Backbone.View.extend({ var hoverTimeoutDate = ""; if (timeoutDate == null) { displayTimeoutDate = $.t('grant.grant-table.never'); - } else if(moment(timeoutDate).isValid()) { + } else if (moment(timeoutDate).isValid()) { timeoutDate = moment(timeoutDate); if (moment().diff(timeoutDate, 'months') < 6) { displayTimeoutDate = timeoutDate.fromNow(); @@ -188,62 +207,80 @@ var ApprovedSiteView = Backbone.View.extend({ hoverTimeoutDate = timeoutDate.format("LLL"); } - - var formattedDate = {displayCreationDate: displayCreationDate, hoverCreationDate: hoverCreationDate, - displayAccessDate: displayAccessDate, hoverAccessDate: hoverAccessDate, - displayTimeoutDate: displayTimeoutDate, hoverTimeoutDate: hoverTimeoutDate}; - - var json = {grant: this.model.toJSON(), client: this.options.client.toJSON(), formattedDate: formattedDate}; - + var formattedDate = { + displayCreationDate: displayCreationDate, + hoverCreationDate: hoverCreationDate, + displayAccessDate: displayAccessDate, + hoverAccessDate: hoverAccessDate, + displayTimeoutDate: displayTimeoutDate, + hoverTimeoutDate: hoverTimeoutDate + }; + + var json = { + grant: this.model.toJSON(), + client: this.options.client.toJSON(), + formattedDate: formattedDate + }; + this.$el.html(this.template(json)); - $('.scope-list', this.el).html(this.scopeTemplate({scopes: this.model.get('allowedScopes'), systemScopes: this.options.systemScopeList})); - - $('.client-more-info-block', this.el).html(this.moreInfoTemplate({client: this.options.client.toJSON()})); - - this.$('.dynamically-registered').tooltip({title: $.t('grant.grant-table.dynamically-registered')}); - this.$('.tokens').tooltip({title: $.t('grant.grant-table.active-tokens')}); - $(this.el).i18n(); + $('.scope-list', this.el).html(this.scopeTemplate({ + scopes: this.model.get('allowedScopes'), + systemScopes: this.options.systemScopeList + })); + + $('.client-more-info-block', this.el).html(this.moreInfoTemplate({ + client: this.options.client.toJSON() + })); + + this.$('.dynamically-registered').tooltip({ + title: $.t('grant.grant-table.dynamically-registered') + }); + this.$('.tokens').tooltip({ + title: $.t('grant.grant-table.active-tokens') + }); + $(this.el).i18n(); return this; }, - + events: { 'click .btn-delete': 'deleteApprovedSite', 'click .toggleMoreInformation': 'toggleMoreInformation' }, - - deleteApprovedSite:function(e) { - e.preventDefault(); + + deleteApprovedSite: function(e) { + e.preventDefault(); if (confirm("Are you sure you want to revoke access to this site?")) { var self = this; - - this.model.destroy({ - dataType: false, processData: false, - success:function () { - self.$el.fadeTo("fast", 0.00, function () { //fade - $(this).slideUp("fast", function () { //slide up - $(this).remove(); //then remove from the DOM - self.parentView.togglePlaceholder(); - }); - }); - }, - error:app.errorHandlerView.handleError() - }); - - this.parentView.delegateEvents(); + + this.model.destroy({ + dataType: false, + processData: false, + success: function() { + self.$el.fadeTo("fast", 0.00, function() { // fade + $(this).slideUp("fast", function() { // slide up + $(this).remove(); // then remove from the DOM + self.parentView.togglePlaceholder(); + }); + }); + }, + error: app.errorHandlerView.handleError() + }); + + this.parentView.delegateEvents(); } - + return false; }, - - toggleMoreInformation:function(e) { + + toggleMoreInformation: function(e) { e.preventDefault(); if ($('.moreInformation', this.el).is(':visible')) { // hide it $('.moreInformation', this.el).hide('fast'); $('.toggleMoreInformation i', this.el).attr('class', 'icon-chevron-right'); $('.moreInformationContainer', this.el).removeClass('alert').removeClass('alert-info').addClass('muted'); - + } else { // show it $('.moreInformation', this.el).show('fast'); @@ -251,36 +288,44 @@ var ApprovedSiteView = Backbone.View.extend({ $('.moreInformationContainer', this.el).addClass('alert').addClass('alert-info').removeClass('muted'); } }, - - close:function() { + + close: function() { $(this.el).unbind(); $(this.el).empty(); } }); -ui.routes.push({path: "user/approved", name: "approvedSites", callback: - +ui.routes.push({ + path: "user/approved", + name: "approvedSites", + callback: + function() { this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('grant.manage-approved-sites'), href:"manage/#user/approve"} - ]); - - this.updateSidebar('user/approved'); - - var view = new ApprovedSiteListView({model:this.approvedSiteList, clientList: this.clientList, systemScopeList: this.systemScopeList}); - view.load( - function(collection, response, options) { - $('#content').html(view.render().el); - setPageTitle($.t('grant.manage-approved-sites')); - } - ); + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('grant.manage-approved-sites'), + href: "manage/#user/approve" + }]); + + this.updateSidebar('user/approved'); + + var view = new ApprovedSiteListView({ + model: this.approvedSiteList, + clientList: this.clientList, + systemScopeList: this.systemScopeList + }); + view.load(function(collection, response, options) { + $('#content').html(view.render().el); + setPageTitle($.t('grant.manage-approved-sites')); + }); } }); ui.templates.push('resources/template/grant.html'); ui.init.push(function(app) { - app.approvedSiteList = new ApprovedSiteCollection(); + app.approvedSiteList = new ApprovedSiteCollection(); }); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/profile.js b/openid-connect-server-webapp/src/main/webapp/resources/js/profile.js index e9fd39ffe2..87b402d142 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/profile.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/profile.js @@ -14,21 +14,28 @@ * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ -ui.routes.push({path: "user/profile", name: "profile", callback: - function() { +ui.routes.push({ + path: "user/profile", + name: "profile", + callback: function() { - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.user-profile.show'), href:"manage/#user/profile"} - ]); - - this.updateSidebar('user/profile'); - - var view = new UserProfileView({model: getUserInfo()}); - $('#content').html(view.render().el); - - setPageTitle($.t('admin.user-profile.show')); - - } + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('admin.user-profile.show'), + href: "manage/#user/profile" + }]); + + this.updateSidebar('user/profile'); + + var view = new UserProfileView({ + model: getUserInfo() + }); + $('#content').html(view.render().el); + + setPageTitle($.t('admin.user-profile.show')); + + } }); \ No newline at end of file diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js b/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js index 3507c65acc..8713c583e7 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/rsreg.js @@ -15,492 +15,565 @@ * limitations under the License. *******************************************************************************/ var ResRegClient = Backbone.Model.extend({ - idAttribute: "client_id", - - defaults:{ - client_id:null, - client_secret:null, - client_name:null, - client_uri:null, - logo_uri:null, - contacts:[], - tos_uri:null, - token_endpoint_auth_method:null, - scope:null, - policy_uri:null, - - jwks_uri:null, - jwks:null, - jwksType:'URI', - - application_type:null, - registration_access_token:null, - registration_client_uri:null - }, - - sync: function(method, model, options){ - if (model.get('registration_access_token')) { - var headers = options.headers ? options.headers : {}; - headers['Authorization'] = 'Bearer ' + model.get('registration_access_token'); - options.headers = headers; - } - - return this.constructor.__super__.sync(method, model, options); - }, - - urlRoot:'resource' - + idAttribute: "client_id", + + defaults: { + client_id: null, + client_secret: null, + client_name: null, + client_uri: null, + logo_uri: null, + contacts: [], + tos_uri: null, + token_endpoint_auth_method: null, + scope: null, + policy_uri: null, + + jwks_uri: null, + jwks: null, + jwksType: 'URI', + + application_type: null, + registration_access_token: null, + registration_client_uri: null + }, + + sync: function(method, model, options) { + if (model.get('registration_access_token')) { + var headers = options.headers ? options.headers : {}; + headers['Authorization'] = 'Bearer ' + model.get('registration_access_token'); + options.headers = headers; + } + + return this.constructor.__super__.sync(method, model, options); + }, + + urlRoot: 'resource' + }); var ResRegRootView = Backbone.View.extend({ - + tagName: 'span', - - initialize:function(options) { - this.options = options; - + + initialize: function(options) { + this.options = options; + }, - - events:{ - "click #newreg":"newReg", - "click #editreg":"editReg" + + events: { + "click #newreg": "newReg", + "click #editreg": "editReg" }, - - load:function(callback) { - if (this.options.systemScopeList.isFetched) { - callback(); - return; - } - - $('#loadingbox').sheet('show'); - $('#loading').html('' + $.t('common.scopes') + ' '); - - $.when(this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}, error:app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - callback(); - }); + + load: function(callback) { + if (this.options.systemScopeList.isFetched) { + callback(); + return; + } + + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t('common.scopes') + ' '); + + $.when(this.options.systemScopeList.fetchIfNeeded({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + callback(); + }); }, - - render:function() { - $(this.el).html($('#tmpl-rsreg').html()); - $(this.el).i18n(); - return this; + + render: function() { + $(this.el).html($('#tmpl-rsreg').html()); + $(this.el).i18n(); + return this; }, - - newReg:function(e) { - e.preventDefault(); - this.remove(); - app.navigate('dev/resource/new', {trigger: true}); + + newReg: function(e) { + e.preventDefault(); + this.remove(); + app.navigate('dev/resource/new', { + trigger: true + }); }, - - editReg:function(e) { - e.preventDefault(); + + editReg: function(e) { + e.preventDefault(); var clientId = $('#clientId').val(); var token = $('#regtoken').val(); - + var client = new ResRegClient({ client_id: clientId, registration_access_token: token }); - + var self = this; - + client.fetch({ success: function() { - - if (client.get("jwks")) { - client.set({ - jwksType: "VAL" - }, { silent: true }); - } else { - client.set({ - jwksType: "URI" - }, { silent: true }); - } - - var view = new ResRegEditView({model: client, systemScopeList: app.systemScopeList}); - - view.load(function() { - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('rsreg.new')); - app.navigate('dev/resource/edit', {trigger: true}); - self.remove(); - }); - }, - error:app.errorHandlerView.handleError({message: $.t('dynreg.invalid-access-token')}) + + if (client.get("jwks")) { + client.set({ + jwksType: "VAL" + }, { + silent: true + }); + } else { + client.set({ + jwksType: "URI" + }, { + silent: true + }); + } + + var view = new ResRegEditView({ + model: client, + systemScopeList: app.systemScopeList + }); + + view.load(function() { + $('#content').html(view.render().el); + view.delegateEvents(); + setPageTitle($.t('rsreg.new')); + app.navigate('dev/resource/edit', { + trigger: true + }); + self.remove(); + }); + }, + error: app.errorHandlerView.handleError({ + message: $.t('dynreg.invalid-access-token') + }) }); } - + }); var ResRegEditView = Backbone.View.extend({ - + tagName: 'span', - - initialize:function(options) { - this.options = options; - if (!this.template) { - this.template = _.template($('#tmpl-rsreg-resource-form').html()); - } - - this.redirectUrisCollection = new Backbone.Collection(); - this.scopeCollection = new Backbone.Collection(); - this.contactsCollection = new Backbone.Collection(); - this.defaultAcrValuesCollection = new Backbone.Collection(); - this.requestUrisCollection = new Backbone.Collection(); - - this.listWidgetViews = []; + + initialize: function(options) { + this.options = options; + if (!this.template) { + this.template = _.template($('#tmpl-rsreg-resource-form').html()); + } + + this.redirectUrisCollection = new Backbone.Collection(); + this.scopeCollection = new Backbone.Collection(); + this.contactsCollection = new Backbone.Collection(); + this.defaultAcrValuesCollection = new Backbone.Collection(); + this.requestUrisCollection = new Backbone.Collection(); + + this.listWidgetViews = []; + }, + + load: function(callback) { + if (this.options.systemScopeList.isFetched) { + callback(); + return; + } + + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t('common.scopes') + ' '); + + $.when(this.options.systemScopeList.fetchIfNeeded({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + callback(); + }); + }, + + events: { + "click .btn-save": "saveClient", + "click .btn-cancel": "cancel", + "click .btn-delete": "deleteClient", + "change #logoUri input": "previewLogo", + "change #tokenEndpointAuthMethod input:radio": "toggleClientCredentials", + "change #jwkSelector input:radio": "toggleJWKSetType" + }, + + cancel: function(e) { + e.preventDefault(); + app.navigate('dev/resource', { + trigger: true + }); + }, + + deleteClient: function(e) { + e.preventDefault(); + + if (confirm($.t('client.client-table.confirm'))) { + var self = this; + + this.model.destroy({ + dataType: false, + processData: false, + success: function() { + self.remove(); + app.navigate('dev/resource', { + trigger: true + }); + }, + error: app.errorHandlerView.handleError() + }); + + } + + return false; + }, + + previewLogo: function() { + if ($('#logoUri input', this.el).val()) { + $('#logoPreview', this.el).empty(); + $('#logoPreview', this.el).attr('src', $('#logoUri input', this.el).val()); + } else { + // $('#logoBlock', this.el).hide(); + $('#logoPreview', this.el).attr('src', 'resources/images/logo_placeholder.gif'); + } + }, + + /** + * Set up the form based on the current state of the tokenEndpointAuthMethod + * parameter + * + * @param event + */ + toggleClientCredentials: function() { + + var tokenEndpointAuthMethod = $('#tokenEndpointAuthMethod input', this.el).filter(':checked').val(); + + // show or hide the signing algorithm method depending on what's + // selected + if (tokenEndpointAuthMethod == 'private_key_jwt' || tokenEndpointAuthMethod == 'client_secret_jwt') { + $('#tokenEndpointAuthSigningAlg', this.el).show(); + } else { + $('#tokenEndpointAuthSigningAlg', this.el).hide(); + } + }, + + /** + * Set up the form based on the JWK Set selector + */ + toggleJWKSetType: function() { + var jwkSelector = $('#jwkSelector input:radio', this.el).filter(':checked').val(); + + if (jwkSelector == 'URI') { + $('#jwksUri', this.el).show(); + $('#jwks', this.el).hide(); + } else if (jwkSelector == 'VAL') { + $('#jwksUri', this.el).hide(); + $('#jwks', this.el).show(); + } else { + $('#jwksUri', this.el).hide(); + $('#jwks', this.el).hide(); + } + + }, + + disableUnsupportedJOSEItems: function(serverSupported, query) { + var supported = ['default']; + if (serverSupported) { + supported = _.union(supported, serverSupported); + } + $(query, this.$el).each(function(idx) { + if (_.contains(supported, $(this).val())) { + $(this).prop('disabled', false); + } else { + $(this).prop('disabled', true); + } + }); + }, - - load:function(callback) { - if (this.options.systemScopeList.isFetched) { - callback(); - return; - } - - $('#loadingbox').sheet('show'); - $('#loading').html('' + $.t('common.scopes') + ' '); - - $.when(this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}, error:app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - callback(); - }); + + // returns "null" if given the value "default" as a string, + // otherwise returns input value. useful for parsing the JOSE + // algorithm dropdowns + defaultToNull: function(value) { + if (value == 'default') { + return null; + } else { + return value; + } }, - - events:{ - "click .btn-save":"saveClient", - "click .btn-cancel":"cancel", - "click .btn-delete":"deleteClient", - "change #logoUri input":"previewLogo", - "change #tokenEndpointAuthMethod input:radio":"toggleClientCredentials", - "change #jwkSelector input:radio":"toggleJWKSetType" - }, - - cancel:function(e) { - e.preventDefault(); - app.navigate('dev/resource', {trigger: true}); - }, - - deleteClient:function (e) { - e.preventDefault(); - - if (confirm($.t('client.client-table.confirm'))) { - var self = this; - - this.model.destroy({ - dataType: false, processData: false, - success:function () { - self.remove(); - app.navigate('dev/resource', {trigger: true}); - }, - error:app.errorHandlerView.handleError() - }); - - } - - return false; - }, - - previewLogo:function() { - if ($('#logoUri input', this.el).val()) { - $('#logoPreview', this.el).empty(); - $('#logoPreview', this.el).attr('src', $('#logoUri input', this.el).val()); - } else { - //$('#logoBlock', this.el).hide(); - $('#logoPreview', this.el).attr('src', 'resources/images/logo_placeholder.gif'); - } - }, - - /** - * Set up the form based on the current state of the tokenEndpointAuthMethod parameter - * @param event - */ - toggleClientCredentials:function() { - - var tokenEndpointAuthMethod = $('#tokenEndpointAuthMethod input', this.el).filter(':checked').val(); - - // show or hide the signing algorithm method depending on what's selected - if (tokenEndpointAuthMethod == 'private_key_jwt' - || tokenEndpointAuthMethod == 'client_secret_jwt') { - $('#tokenEndpointAuthSigningAlg', this.el).show(); - } else { - $('#tokenEndpointAuthSigningAlg', this.el).hide(); - } - }, - - /** - * Set up the form based on the JWK Set selector - */ - toggleJWKSetType:function() { - var jwkSelector = $('#jwkSelector input:radio', this.el).filter(':checked').val(); - - if (jwkSelector == 'URI') { - $('#jwksUri', this.el).show(); - $('#jwks', this.el).hide(); - } else if (jwkSelector == 'VAL') { - $('#jwksUri', this.el).hide(); - $('#jwks', this.el).show(); - } else { - $('#jwksUri', this.el).hide(); - $('#jwks', this.el).hide(); - } - - }, - - disableUnsupportedJOSEItems:function(serverSupported, query) { - var supported = ['default']; - if (serverSupported) { - supported = _.union(supported, serverSupported); - } - $(query, this.$el).each(function(idx) { - if(_.contains(supported, $(this).val())) { - $(this).prop('disabled', false); - } else { - $(this).prop('disabled', true); - } - }); - - }, - - // returns "null" if given the value "default" as a string, otherwise returns input value. useful for parsing the JOSE algorithm dropdowns - defaultToNull:function(value) { - if (value == 'default') { - return null; - } else { - return value; - } - }, - - saveClient:function (e) { - e.preventDefault(); - - $('.control-group').removeClass('error'); - - // sync any leftover collection items - _.each(this.listWidgetViews, function(v) { - v.addItem($.Event('click')); - }); - - // build the scope object - var scopes = this.scopeCollection.pluck("item").join(" "); - - var contacts = this.contactsCollection.pluck('item'); - var userInfo = getUserInfo(); - if (userInfo && userInfo.email) { - if (!_.contains(contacts, userInfo.email)) { - contacts.push(userInfo.email); - } - } - - // process the JWKS - var jwksUri = null; - var jwks = null; - var jwkSelector = $('#jwkSelector input:radio', this.el).filter(':checked').val(); - - if (jwkSelector == 'URI') { - jwksUri = $('#jwksUri input').val(); - jwks = null; - } else if (jwkSelector == 'VAL') { - jwksUri = null; - try { - jwks = JSON.parse($('#jwks textarea').val()); - } catch (e) { - console.log("An error occurred when parsing the JWK Set"); - - //Display an alert with an error message - app.errorHandlerView.showErrorMessage($.t("client.client-form.error.jwk-set"), $.t("client.client-form.error.jwk-set-parse")); - return false; - } - } else { - jwksUri = null; - jwks = null; - } - - var attrs = { - client_name:$('#clientName input').val(), - logo_uri:$('#logoUri input').val(), - scope: scopes, - client_secret: null, // never send a client secret - tos_uri: $('#tosUri input').val(), - policy_uri: $('#policyUri input').val(), - client_uri: $('#clientUri input').val(), - application_type: $('#applicationType input').filter(':checked').val(), - jwks_uri: jwksUri, - jwks: jwks, - token_endpoint_auth_method: $('#tokenEndpointAuthMethod input').filter(':checked').val(), - contacts: contacts, - token_endpoint_auth_signing_alg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val()) - }; - - // set all empty strings to nulls - for (var key in attrs) { - if (attrs[key] === "") { - attrs[key] = null; - } - } - - var _self = this; - this.model.save(attrs, { - success:function () { - // switch to an "edit" view - app.navigate('dev/resource/edit', {trigger: true}); - _self.remove(); - - if (_self.model.get("jwks")) { - _self.model.set({ - jwksType: "VAL" - }, { silent: true }); - } else { - _self.model.set({ - jwksType: "URI" - }, { silent: true }); - } - - var view = new ResRegEditView({model: _self.model, systemScopeList: _self.options.systemScopeList}); - - view.load(function() { - // reload - $('#content').html(view.render().el); - view.delegateEvents(); - }); - }, - error:app.errorHandlerView.handleError() - }); - - return false; - }, - - render:function() { - $(this.el).html(this.template({client: this.model.toJSON(), userInfo: getUserInfo()})); - + + saveClient: function(e) { + e.preventDefault(); + + $('.control-group').removeClass('error'); + + // sync any leftover collection items + _.each(this.listWidgetViews, function(v) { + v.addItem($.Event('click')); + }); + + // build the scope object + var scopes = this.scopeCollection.pluck("item").join(" "); + + var contacts = this.contactsCollection.pluck('item'); + var userInfo = getUserInfo(); + if (userInfo && userInfo.email) { + if (!_.contains(contacts, userInfo.email)) { + contacts.push(userInfo.email); + } + } + + // process the JWKS + var jwksUri = null; + var jwks = null; + var jwkSelector = $('#jwkSelector input:radio', this.el).filter(':checked').val(); + + if (jwkSelector == 'URI') { + jwksUri = $('#jwksUri input').val(); + jwks = null; + } else if (jwkSelector == 'VAL') { + jwksUri = null; + try { + jwks = JSON.parse($('#jwks textarea').val()); + } catch (e) { + console.log("An error occurred when parsing the JWK Set"); + + // Display an alert with an error message + app.errorHandlerView.showErrorMessage($.t("client.client-form.error.jwk-set"), $.t("client.client-form.error.jwk-set-parse")); + return false; + } + } else { + jwksUri = null; + jwks = null; + } + + var attrs = { + client_name: $('#clientName input').val(), + logo_uri: $('#logoUri input').val(), + scope: scopes, + client_secret: null, // never send a client secret + tos_uri: $('#tosUri input').val(), + policy_uri: $('#policyUri input').val(), + client_uri: $('#clientUri input').val(), + application_type: $('#applicationType input').filter(':checked').val(), + jwks_uri: jwksUri, + jwks: jwks, + token_endpoint_auth_method: $('#tokenEndpointAuthMethod input').filter(':checked').val(), + contacts: contacts, + token_endpoint_auth_signing_alg: this.defaultToNull($('#tokenEndpointAuthSigningAlg select').val()) + }; + + // set all empty strings to nulls + for ( var key in attrs) { + if (attrs[key] === "") { + attrs[key] = null; + } + } + + var _self = this; + this.model.save(attrs, { + success: function() { + // switch to an "edit" view + app.navigate('dev/resource/edit', { + trigger: true + }); + _self.remove(); + + if (_self.model.get("jwks")) { + _self.model.set({ + jwksType: "VAL" + }, { + silent: true + }); + } else { + _self.model.set({ + jwksType: "URI" + }, { + silent: true + }); + } + + var view = new ResRegEditView({ + model: _self.model, + systemScopeList: _self.options.systemScopeList + }); + + view.load(function() { + // reload + $('#content').html(view.render().el); + view.delegateEvents(); + }); + }, + error: app.errorHandlerView.handleError() + }); + + return false; + }, + + render: function() { + $(this.el).html(this.template({ + client: this.model.toJSON(), + userInfo: getUserInfo() + })); + this.listWidgetViews = []; - + var _self = this; - // build and bind scopes - var scopes = this.model.get("scope"); - var scopeSet = scopes ? scopes.split(" ") : []; - _.each(scopeSet, function (scope) { - _self.scopeCollection.add(new Backbone.Model({item:scope})); - }); - - var scopeView = new ListWidgetView({ - placeholder: $.t('client.client-form.scope-placeholder'), - autocomplete: _.uniq(_.flatten(this.options.systemScopeList.unrestrictedScopes().pluck("value"))), - helpBlockText: $.t('rsreg.client-form.scope-help'), - collection: this.scopeCollection}); - $("#scope .controls",this.el).html(scopeView.render().el); - this.listWidgetViews.push(scopeView); - - // build and bind contacts - _.each(this.model.get('contacts'), function (contact) { - _self.contactsCollection.add(new Backbone.Model({item:contact})); - }); - - var contactView = new ListWidgetView({ - placeholder: $.t('client.client-form.contacts-placeholder'), - helpBlockText: $.t('client.client-form.contacts-help'), - collection: this.contactsCollection}); - $("#contacts .controls", this.el).html(contactView.render().el); - this.listWidgetViews.push(contactView); - - - this.toggleClientCredentials(); - this.previewLogo(); - this.toggleJWKSetType(); - - // disable unsupported JOSE algorithms - this.disableUnsupportedJOSEItems(app.serverConfiguration.token_endpoint_auth_signing_alg_values_supported, '#tokenEndpointAuthSigningAlg option'); - - this.$('.nyi').clickover({ - placement: 'right', - title: $.t('common.not-yet-implemented'), - content: $.t('common.not-yet-implemented-content') - }); - - - $(this.el).i18n(); - return this; + // build and bind scopes + var scopes = this.model.get("scope"); + var scopeSet = scopes ? scopes.split(" ") : []; + _.each(scopeSet, function(scope) { + _self.scopeCollection.add(new Backbone.Model({ + item: scope + })); + }); + + var scopeView = new ListWidgetView({ + placeholder: $.t('client.client-form.scope-placeholder'), + autocomplete: _.uniq(_.flatten(this.options.systemScopeList.unrestrictedScopes().pluck("value"))), + helpBlockText: $.t('rsreg.client-form.scope-help'), + collection: this.scopeCollection + }); + $("#scope .controls", this.el).html(scopeView.render().el); + this.listWidgetViews.push(scopeView); + + // build and bind contacts + _.each(this.model.get('contacts'), function(contact) { + _self.contactsCollection.add(new Backbone.Model({ + item: contact + })); + }); + + var contactView = new ListWidgetView({ + placeholder: $.t('client.client-form.contacts-placeholder'), + helpBlockText: $.t('client.client-form.contacts-help'), + collection: this.contactsCollection + }); + $("#contacts .controls", this.el).html(contactView.render().el); + this.listWidgetViews.push(contactView); + + this.toggleClientCredentials(); + this.previewLogo(); + this.toggleJWKSetType(); + + // disable unsupported JOSE algorithms + this.disableUnsupportedJOSEItems(app.serverConfiguration.token_endpoint_auth_signing_alg_values_supported, '#tokenEndpointAuthSigningAlg option'); + + this.$('.nyi').clickover({ + placement: 'right', + title: $.t('common.not-yet-implemented'), + content: $.t('common.not-yet-implemented-content') + }); + + $(this.el).i18n(); + return this; } - + }); -ui.routes.push({path: "dev/resource", name: "resReg", callback: - function() { +ui.routes.push({ + path: "dev/resource", + name: "resReg", + callback: function() { this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-resource'), href:"manage/#dev/resource"} - ]); - - this.updateSidebar('dev/resource'); - - var view = new ResRegRootView({systemScopeList: this.systemScopeList}); + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('admin.self-service-resource'), + href: "manage/#dev/resource" + }]); + + this.updateSidebar('dev/resource'); + + var view = new ResRegRootView({ + systemScopeList: this.systemScopeList + }); view.load(function() { - $('#content').html(view.render().el); - - setPageTitle($.t('admin.self-service-resource')); + $('#content').html(view.render().el); + + setPageTitle($.t('admin.self-service-resource')); }); - + } }); -ui.routes.push({path: "dev/resource/new", name: "newResReg", callback: - function() { - +ui.routes.push({ + path: "dev/resource/new", + name: "newResReg", + callback: function() { + this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-resource'), href:"manage/#dev/resource"}, - {text:$.t('rsreg.new'), href:"manage/#dev/resource/new"} - ]); - - this.updateSidebar('dev/resource'); - + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('admin.self-service-resource'), + href: "manage/#dev/resource" + }, { + text: $.t('rsreg.new'), + href: "manage/#dev/resource/new" + }]); + + this.updateSidebar('dev/resource'); + var client = new ResRegClient(); - var view = new ResRegEditView({model: client, systemScopeList:this.systemScopeList}); - + var view = new ResRegEditView({ + model: client, + systemScopeList: this.systemScopeList + }); + view.load(function() { - + var userInfo = getUserInfo(); var contacts = []; if (userInfo != null && userInfo.email != null) { contacts.push(userInfo.email); } - + client.set({ - scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), - token_endpoint_auth_method: 'client_secret_basic', - contacts: contacts - }, { silent: true }); - + scope: _.uniq(_.flatten(app.systemScopeList.defaultUnrestrictedScopes().pluck("value"))).join(" "), + token_endpoint_auth_method: 'client_secret_basic', + contacts: contacts + }, { + silent: true + }); + $('#content').html(view.render().el); view.delegateEvents(); setPageTitle($.t('rsreg.new')); - + }); - + } }); -ui.routes.push({path: "dev/resource/edit", name: "editResReg", callback: - function() { +ui.routes.push({ + path: "dev/resource/edit", + name: "editResReg", + callback: function() { this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('admin.self-service-resource'), href:"manage/#dev/resource"}, - {text:$.t('rsreg.edit'), href:"manage/#dev/resource/edit"} - ]); - - this.updateSidebar('dev/resource'); - + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('admin.self-service-resource'), + href: "manage/#dev/resource" + }, { + text: $.t('rsreg.edit'), + href: "manage/#dev/resource/edit" + }]); + + this.updateSidebar('dev/resource'); + setPageTitle($.t('rsreg.edit')); - // note that this doesn't actually load the client, that's supposed to happen elsewhere... + // note that this doesn't actually load the client, that's supposed to + // happen elsewhere... } }); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js b/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js index aced6de280..867f89f8ad 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/scope.js @@ -17,23 +17,23 @@ var SystemScopeModel = Backbone.Model.extend({ idAttribute: 'id', - defaults:{ - id:null, - description:null, - icon:null, - value:null, - defaultScope:false, - restricted:false + defaults: { + id: null, + description: null, + icon: null, + value: null, + defaultScope: false, + restricted: false }, - + urlRoot: 'api/scopes' }); var SystemScopeCollection = Backbone.Collection.extend({ idAttribute: 'id', - + model: SystemScopeModel, - + url: 'api/scopes', defaultScopes: function() { @@ -42,149 +42,164 @@ var SystemScopeCollection = Backbone.Collection.extend({ }); return new SystemScopeCollection(filtered); }, - + unrestrictedScopes: function() { filtered = this.filter(function(scope) { return scope.get("restricted") !== true; }); return new SystemScopeCollection(filtered); }, - + defaultUnrestrictedScopes: function() { filtered = this.filter(function(scope) { return scope.get("defaultScope") === true && scope.get("restricted") !== true; }); return new SystemScopeCollection(filtered); }, - + getByValue: function(value) { - var scopes = this.where({value: value}); + var scopes = this.where({ + value: value + }); if (scopes.length == 1) { return scopes[0]; } else { return null; } } - + }); var SystemScopeView = Backbone.View.extend({ - + tagName: 'tr', - - initialize:function (options) { - this.options = options; - - if (!this.template) { - this.template = _.template($('#tmpl-system-scope').html()); - } - - this.model.bind('change', this.render, this); - - }, - - events: { - 'click .btn-edit':'editScope', - 'click .btn-delete':'deleteScope' + + initialize: function(options) { + this.options = options; + + if (!this.template) { + this.template = _.template($('#tmpl-system-scope').html()); + } + + this.model.bind('change', this.render, this); + }, - - editScope:function(e) { - e.preventDefault(); - app.navigate('admin/scope/' + this.model.id, {trigger: true}); + + events: { + 'click .btn-edit': 'editScope', + 'click .btn-delete': 'deleteScope' }, - - render:function (eventName) { - this.$el.html(this.template(this.model.toJSON())); - - $('.restricted', this.el).tooltip({title: $.t('scope.system-scope-table.tooltip-restricted')}); - $('.default', this.el).tooltip({title: $.t('scope.system-scope-table.tooltip-default')}); - - $(this.el).i18n(); - return this; - }, - - deleteScope:function (e) { - e.preventDefault(); - - if (confirm($.t("scope.system-scope-table.confirm"))) { - var _self = this; - - this.model.destroy({ - dataType: false, processData: false, - success:function () { - - _self.$el.fadeTo("fast", 0.00, function () { //fade - $(this).slideUp("fast", function () { //slide up - $(this).remove(); //then remove from the DOM - _self.parentView.togglePlaceholder(); - }); - }); - }, - error:app.errorHandlerView.handleError() - }); - - _self.parentView.delegateEvents(); - } - - return false; - }, - - close:function () { - $(this.el).unbind(); - $(this.el).empty(); - } + + editScope: function(e) { + e.preventDefault(); + app.navigate('admin/scope/' + this.model.id, { + trigger: true + }); + }, + + render: function(eventName) { + this.$el.html(this.template(this.model.toJSON())); + + $('.restricted', this.el).tooltip({ + title: $.t('scope.system-scope-table.tooltip-restricted') + }); + $('.default', this.el).tooltip({ + title: $.t('scope.system-scope-table.tooltip-default') + }); + + $(this.el).i18n(); + return this; + }, + + deleteScope: function(e) { + e.preventDefault(); + + if (confirm($.t("scope.system-scope-table.confirm"))) { + var _self = this; + + this.model.destroy({ + dataType: false, + processData: false, + success: function() { + + _self.$el.fadeTo("fast", 0.00, function() { // fade + $(this).slideUp("fast", function() { // slide up + $(this).remove(); // then remove from the DOM + _self.parentView.togglePlaceholder(); + }); + }); + }, + error: app.errorHandlerView.handleError() + }); + + _self.parentView.delegateEvents(); + } + + return false; + }, + + close: function() { + $(this.el).unbind(); + $(this.el).empty(); + } }); var SystemScopeListView = Backbone.View.extend({ tagName: 'span', - - initialize:function(options) { - this.options = options; + + initialize: function(options) { + this.options = options; + }, + + load: function(callback) { + if (this.model.isFetched) { + callback(); + return; + } + + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t('common.scopes') + ' '); + + $.when(this.model.fetchIfNeeded({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + callback(); + }); }, - - load:function(callback) { - if (this.model.isFetched) { - callback(); - return; - } - - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t('common.scopes') + ' ' - ); - - $.when(this.model.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}, error:app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - callback(); - }); - }, - - events:{ - "click .new-scope":"newScope", - "click .refresh-table":"refreshTable" + + events: { + "click .new-scope": "newScope", + "click .refresh-table": "refreshTable" }, - newScope:function(e) { + newScope: function(e) { this.remove(); - app.navigate('admin/scope/new', {trigger: true}); + app.navigate('admin/scope/new', { + trigger: true + }); }, - - refreshTable:function(e) { + + refreshTable: function(e) { var _self = this; - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t('common.scopes') + ' ' - ); - - $.when(this.model.fetch({success:function(e) {$('#loading-scopes').addClass('label-success');}, error:app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - _self.render(); - }); + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t('common.scopes') + ' '); + + $.when(this.model.fetch({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + _self.render(); + }); }, - - togglePlaceholder:function() { + + togglePlaceholder: function() { if (this.model.length > 0) { $('#scope-table', this.el).show(); $('#scope-table-empty', this.el).hide(); @@ -193,249 +208,271 @@ var SystemScopeListView = Backbone.View.extend({ $('#scope-table-empty', this.el).show(); } }, - - render: function (eventName) { - + + render: function(eventName) { + // append and render the table structure $(this.el).html($('#tmpl-system-scope-table').html()); - + var _self = this; - - _.each(this.model.models, function (scope) { - var view = new SystemScopeView({model: scope}); + + _.each(this.model.models, function(scope) { + var view = new SystemScopeView({ + model: scope + }); view.parentView = _self; $("#scope-table", _self.el).append(view.render().el); }, this); - + this.togglePlaceholder(); - $(this.el).i18n(); + $(this.el).i18n(); return this; } }); -var SystemScopeFormView = Backbone.View.extend({ - tagName: 'span', - - initialize:function(options) { - this.options = options; - if (!this.template) { - this.template = _.template($('#tmpl-system-scope-form').html()); - } - if (!this.iconTemplate) { - this.iconTemplate = _.template($('#tmpl-system-scope-icon').html()); - } - - // initialize our icon set into slices for the selector - if (!this.bootstrapIcons) { - this.bootstrapIcons = []; - - var iconList = ['glass', 'music', 'search', 'envelope', 'heart', 'star', 'star-empty', - 'user', 'film', 'th-large', 'th', 'th-list', 'ok', 'remove', 'zoom-in', - 'zoom-out', 'off', 'signal', 'cog', 'trash', 'home', 'file', 'time', 'road', - 'download-alt', 'download', 'upload', 'inbox', 'play-circle', 'repeat', - 'refresh', 'list-alt', 'lock', 'flag', 'headphones', 'volume-off', - 'volume-down', 'volume-up', 'qrcode', 'barcode', 'tag', 'tags', 'book', - 'bookmark', 'print', 'camera', 'font', 'bold', 'italic', 'text-height', - 'text-width', 'align-left', 'align-center', 'align-right', 'align-justify', - 'list', 'indent-left', 'indent-right', 'facetime-video', 'picture', 'pencil', - 'map-marker', 'adjust', 'tint', 'edit', 'share', 'check', 'move', 'step-backward', - 'fast-backward', 'backward', 'play', 'pause', 'stop', 'forward', 'fast-forward', - 'step-forward', 'eject', 'chevron-left', 'chevron-right', 'plus-sign', - 'minus-sign', 'remove-sign', 'ok-sign', 'question-sign', 'info-sign', - 'screenshot', 'remove-circle', 'ok-circle', 'ban-circle', 'arrow-left', - 'arrow-right', 'arrow-up', 'arrow-down', 'share-alt', 'resize-full', 'resize-small', - 'plus', 'minus', 'asterisk', 'exclamation-sign', 'gift', 'leaf', 'fire', - 'eye-open', 'eye-close', 'warning-sign', 'plane', 'calendar', 'random', - 'comment', 'magnet', 'chevron-up', 'chevron-down', 'retweet', 'shopping-cart', - 'folder-close', 'folder-open', 'resize-vertical', 'resize-horizontal', - 'hdd', 'bullhorn', 'bell', 'certificate', 'thumbs-up', 'thumbs-down', - 'hand-right', 'hand-left', 'hand-up', 'hand-down', 'circle-arrow-right', - 'circle-arrow-left', 'circle-arrow-up', 'circle-arrow-down', 'globe', - 'wrench', 'tasks', 'filter', 'briefcase', 'fullscreen']; - - var size = 3; - while (iconList.length > 0) { - this.bootstrapIcons.push(iconList.splice(0, size)); - } +var SystemScopeFormView = Backbone.View + .extend({ + tagName: 'span', - } - }, - - events:{ - 'click .btn-save':'saveScope', - 'click .btn-cancel': function() {app.navigate('admin/scope', {trigger: true}); }, - 'click .btn-icon':'selectIcon' - }, - - load:function(callback) { - if (this.model.isFetched) { - callback(); - return; - } - - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t("common.scopes") + ' ' - ); - - $.when(this.model.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}, error:app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - callback(); - }); - - }, - - saveScope:function(e) { - e.preventDefault(); - - var value = $('#value input').val(); - - if (value == null || value.trim() == "") { - // error: can't have a blank scope - return false; - } - - var valid = this.model.set({ - value:value, - description:$('#description textarea').val(), - icon:$('#iconDisplay input').val(), - defaultScope:$('#defaultScope input').is(':checked'), - restricted:$('#restricted input').is(':checked') - }); - - if (valid) { - - var _self = this; - this.model.save({}, { - success:function() { - app.systemScopeList.add(_self.model); - app.navigate('admin/scope', {trigger: true}); + initialize: function(options) { + this.options = options; + if (!this.template) { + this.template = _.template($('#tmpl-system-scope-form').html()); + } + if (!this.iconTemplate) { + this.iconTemplate = _.template($('#tmpl-system-scope-icon').html()); + } + + // initialize our icon set into slices for the selector + if (!this.bootstrapIcons) { + this.bootstrapIcons = []; + + var iconList = ['glass', 'music', 'search', 'envelope', 'heart', 'star', 'star-empty', 'user', 'film', 'th-large', 'th', 'th-list', 'ok', 'remove', 'zoom-in', 'zoom-out', 'off', 'signal', 'cog', 'trash', 'home', 'file', 'time', 'road', 'download-alt', 'download', 'upload', 'inbox', 'play-circle', 'repeat', 'refresh', 'list-alt', 'lock', + 'flag', 'headphones', 'volume-off', 'volume-down', 'volume-up', 'qrcode', 'barcode', 'tag', 'tags', 'book', 'bookmark', 'print', 'camera', 'font', 'bold', 'italic', 'text-height', 'text-width', 'align-left', 'align-center', 'align-right', 'align-justify', 'list', 'indent-left', 'indent-right', 'facetime-video', 'picture', + 'pencil', 'map-marker', 'adjust', 'tint', 'edit', 'share', 'check', 'move', 'step-backward', 'fast-backward', 'backward', 'play', 'pause', 'stop', 'forward', 'fast-forward', 'step-forward', 'eject', 'chevron-left', 'chevron-right', 'plus-sign', 'minus-sign', 'remove-sign', 'ok-sign', 'question-sign', 'info-sign', 'screenshot', + 'remove-circle', 'ok-circle', 'ban-circle', 'arrow-left', 'arrow-right', 'arrow-up', 'arrow-down', 'share-alt', 'resize-full', 'resize-small', 'plus', 'minus', 'asterisk', 'exclamation-sign', 'gift', 'leaf', 'fire', 'eye-open', 'eye-close', 'warning-sign', 'plane', 'calendar', 'random', 'comment', 'magnet', 'chevron-up', + 'chevron-down', 'retweet', 'shopping-cart', 'folder-close', 'folder-open', 'resize-vertical', 'resize-horizontal', 'hdd', 'bullhorn', 'bell', 'certificate', 'thumbs-up', 'thumbs-down', 'hand-right', 'hand-left', 'hand-up', 'hand-down', 'circle-arrow-right', 'circle-arrow-left', 'circle-arrow-up', 'circle-arrow-down', 'globe', + 'wrench', 'tasks', 'filter', 'briefcase', 'fullscreen']; + + var size = 3; + while (iconList.length > 0) { + this.bootstrapIcons.push(iconList.splice(0, size)); + } + + } + }, + + events: { + 'click .btn-save': 'saveScope', + 'click .btn-cancel': function() { + app.navigate('admin/scope', { + trigger: true + }); }, - error:app.errorHandlerView.handleError() - }); - } + 'click .btn-icon': 'selectIcon' + }, - return false; - }, - - selectIcon:function(e) { - e.preventDefault(); - - var icon = e.target.value; - - $('#iconDisplay input').val(icon); - $('#iconDisplay #iconName').html(icon); - $('#iconDisplay i').removeClass(); - $('#iconDisplay i').addClass('icon-' + icon); - $('#iconDisplay i').addClass('icon-white'); - - $('#iconSelector').modal('hide'); - - return false; - }, - - render: function(eventName) { - this.$el.html(this.template(this.model.toJSON())); - - _.each(this.bootstrapIcons, function (items) { - $("#iconSelector .modal-body", this.el).append(this.iconTemplate({items:items})); - }, this); - - $(this.el).i18n(); - return this; - } -}); + load: function(callback) { + if (this.model.isFetched) { + callback(); + return; + } + + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t("common.scopes") + ' '); + + $.when(this.model.fetchIfNeeded({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + callback(); + }); + + }, + + saveScope: function(e) { + e.preventDefault(); + + var value = $('#value input').val(); + + if (value == null || value.trim() == "") { + // error: can't have a blank scope + return false; + } + + var valid = this.model.set({ + value: value, + description: $('#description textarea').val(), + icon: $('#iconDisplay input').val(), + defaultScope: $('#defaultScope input').is(':checked'), + restricted: $('#restricted input').is(':checked') + }); + + if (valid) { + + var _self = this; + this.model.save({}, { + success: function() { + app.systemScopeList.add(_self.model); + app.navigate('admin/scope', { + trigger: true + }); + }, + error: app.errorHandlerView.handleError() + }); + } + + return false; + }, + + selectIcon: function(e) { + e.preventDefault(); + + var icon = e.target.value; + + $('#iconDisplay input').val(icon); + $('#iconDisplay #iconName').html(icon); + $('#iconDisplay i').removeClass(); + $('#iconDisplay i').addClass('icon-' + icon); + $('#iconDisplay i').addClass('icon-white'); + + $('#iconSelector').modal('hide'); + + return false; + }, + + render: function(eventName) { + this.$el.html(this.template(this.model.toJSON())); + + _.each(this.bootstrapIcons, function(items) { + $("#iconSelector .modal-body", this.el).append(this.iconTemplate({ + items: items + })); + }, this); + + $(this.el).i18n(); + return this; + } + }); + +ui.routes.push({ + path: "admin/scope", + name: "siteScope", + callback: function() { -ui.routes.push({path: "admin/scope", name: "siteScope", callback: - function() { - if (!isAdmin()) { this.root(); return; } - + this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('scope.manage'), href:"manage/#admin/scope"} - ]); - - this.updateSidebar('admin/scope'); - - var view = new SystemScopeListView({model:this.systemScopeList}); - + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('scope.manage'), + href: "manage/#admin/scope" + }]); + + this.updateSidebar('admin/scope'); + + var view = new SystemScopeListView({ + model: this.systemScopeList + }); + view.load(function() { $('#content').html(view.render().el); view.delegateEvents(); - setPageTitle($.t('scope.manage')); + setPageTitle($.t('scope.manage')); }); - + } }); -ui.routes.push({path: "admin/scope/new", name:"newScope", callback: - function() { - - +ui.routes.push({ + path: "admin/scope/new", + name: "newScope", + callback: function() { + if (!isAdmin()) { this.root(); return; } - + this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('scope.manage'), href:"manage/#admin/scope"}, - {text:$.t('scope.system-scope-form.new'), href:"manage/#admin/scope/new"} - ]); - - this.updateSidebar('admin/scope'); - + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('scope.manage'), + href: "manage/#admin/scope" + }, { + text: $.t('scope.system-scope-form.new'), + href: "manage/#admin/scope/new" + }]); + + this.updateSidebar('admin/scope'); + var scope = new SystemScopeModel(); - - var view = new SystemScopeFormView({model:scope}); + + var view = new SystemScopeFormView({ + model: scope + }); view.load(function() { $('#content').html(view.render().el); setPageTitle($.t('scope.system-scope-form.new')); }); - + } }); -ui.routes.push({path: "admin/scope/:id", name: "editScope", callback: - function(sid) { +ui.routes.push({ + path: "admin/scope/:id", + name: "editScope", + callback: function(sid) { if (!isAdmin()) { this.root(); return; } - + this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('scope.manage'), href:"manage/#admin/scope"}, - {text:$.t('scope.system-scope-form.edit'), href:"manage/#admin/scope/" + sid} - ]); - - this.updateSidebar('admin/scope'); - + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('scope.manage'), + href: "manage/#admin/scope" + }, { + text: $.t('scope.system-scope-form.edit'), + href: "manage/#admin/scope/" + sid + }]); + + this.updateSidebar('admin/scope'); + var scope = this.systemScopeList.get(sid); if (!scope) { - scope = new SystemScopeModel({id: sid}); + scope = new SystemScopeModel({ + id: sid + }); } - - var view = new SystemScopeFormView({model:scope}); + + var view = new SystemScopeFormView({ + model: scope + }); view.load(function() { $('#content').html(view.render().el); setPageTitle($.t('scope.system-scope-form.new')); }); - + } }); ui.templates.push('resources/template/scope.html'); ui.init.push(function(app) { - app.systemScopeList = new SystemScopeCollection(); + app.systemScopeList = new SystemScopeCollection(); }); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/token.js b/openid-connect-server-webapp/src/main/webapp/resources/js/token.js index ed012a620a..36112eb3c3 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/token.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/token.js @@ -18,59 +18,59 @@ var AccessTokenModel = Backbone.Model.extend({ idAttribute: 'id', - defaults:{ - id:null, - value:null, - refreshTokenId:null, - scopes:[], - clientId:null, - userId:null, - expiration:null + defaults: { + id: null, + value: null, + refreshTokenId: null, + scopes: [], + clientId: null, + userId: null, + expiration: null }, - + urlRoot: 'api/tokens/access' }); var AccessTokenCollection = Backbone.Collection.extend({ idAttribute: 'id', - + model: AccessTokenModel, - + url: 'api/tokens/access' - + }); var AccessTokenView = Backbone.View.extend({ - + tagName: 'tr', - - initialize:function (options) { - this.options = options; - - if (!this.template) { - this.template = _.template($('#tmpl-access-token').html()); - } - - if (!this.scopeTemplate) { - this.scopeTemplate = _.template($('#tmpl-scope-list').html()); - } - - if (!this.moreInfoTemplate) { - this.moreInfoTemplate = _.template($('#tmpl-client-more-info-block').html()); - } - - this.model.bind('change', this.render, this); - - }, - - events: { - 'click .btn-delete':'deleteToken', - 'click .token-substring':'showTokenValue', + + initialize: function(options) { + this.options = options; + + if (!this.template) { + this.template = _.template($('#tmpl-access-token').html()); + } + + if (!this.scopeTemplate) { + this.scopeTemplate = _.template($('#tmpl-scope-list').html()); + } + + if (!this.moreInfoTemplate) { + this.moreInfoTemplate = _.template($('#tmpl-client-more-info-block').html()); + } + + this.model.bind('change', this.render, this); + + }, + + events: { + 'click .btn-delete': 'deleteToken', + 'click .token-substring': 'showTokenValue', 'click .toggleMoreInformation': 'toggleMoreInformation' }, - - render:function (eventName) { - + + render: function(eventName) { + var expirationDate = this.model.get("expiration"); if (expirationDate == null) { @@ -80,59 +80,70 @@ var AccessTokenView = Backbone.View.extend({ } else { expirationDate = moment(expirationDate).calendar(); } - - var json = {token: this.model.toJSON(), client: this.options.client.toJSON(), formattedExpiration: expirationDate}; + + var json = { + token: this.model.toJSON(), + client: this.options.client.toJSON(), + formattedExpiration: expirationDate + }; this.$el.html(this.template(json)); // hide full value - $('.token-full', this.el).hide(); - + $('.token-full', this.el).hide(); + // show scopes - $('.scope-list', this.el).html(this.scopeTemplate({scopes: this.model.get('scopes'), systemScopes: this.options.systemScopeList})); - - $('.client-more-info-block', this.el).html(this.moreInfoTemplate({client: this.options.client.toJSON()})); - - $(this.el).i18n(); - return this; - }, - - deleteToken:function (e) { - e.preventDefault(); - - if (confirm($.t("token.token-table.confirm"))) { - - var _self = this; - - this.model.destroy({ - dataType: false, processData: false, - success:function () { - - _self.$el.fadeTo("fast", 0.00, function () { //fade - $(this).slideUp("fast", function () { //slide up - $(this).remove(); //then remove from the DOM - // refresh the table in case we removed an id token, too - _self.parentView.refreshTable(); - }); - }); - }, - error:app.errorHandlerView.handleError() - }); - - this.parentView.delegateEvents(); - } - - return false; - }, - - toggleMoreInformation:function(e) { + $('.scope-list', this.el).html(this.scopeTemplate({ + scopes: this.model.get('scopes'), + systemScopes: this.options.systemScopeList + })); + + $('.client-more-info-block', this.el).html(this.moreInfoTemplate({ + client: this.options.client.toJSON() + })); + + $(this.el).i18n(); + return this; + }, + + deleteToken: function(e) { + e.preventDefault(); + + if (confirm($.t("token.token-table.confirm"))) { + + var _self = this; + + this.model.destroy({ + dataType: false, + processData: false, + success: function() { + + _self.$el.fadeTo("fast", 0.00, function() { // fade + $(this).slideUp("fast", function() { // slide up + $(this).remove(); // then remove from the DOM + // refresh the table in case we removed an id token, + // too + _self.parentView.refreshTable(); + }); + }); + }, + error: app.errorHandlerView.handleError() + }); + + this.parentView.delegateEvents(); + } + + return false; + }, + + toggleMoreInformation: function(e) { e.preventDefault(); if ($('.moreInformation', this.el).is(':visible')) { // hide it $('.moreInformation', this.el).hide('fast'); $('.toggleMoreInformation i', this.el).attr('class', 'icon-chevron-right'); $('.moreInformationContainer', this.el).removeClass('alert').removeClass('alert-info').addClass('muted'); - + } else { // show it $('.moreInformation', this.el).show('fast'); @@ -141,73 +152,73 @@ var AccessTokenView = Backbone.View.extend({ } }, - close:function () { - $(this.el).unbind(); - $(this.el).empty(); - }, - - showTokenValue:function (e) { - e.preventDefault(); - $('.token-substring', this.el).hide(); - $('.token-full', this.el).show(); - } + close: function() { + $(this.el).unbind(); + $(this.el).empty(); + }, + + showTokenValue: function(e) { + e.preventDefault(); + $('.token-substring', this.el).hide(); + $('.token-full', this.el).show(); + } }); var RefreshTokenModel = Backbone.Model.extend({ idAttribute: 'id', - defaults:{ - id:null, - value:null, - scopes:[], - clientId:null, - userId:null, - expiration:null + defaults: { + id: null, + value: null, + scopes: [], + clientId: null, + userId: null, + expiration: null }, - + urlRoot: 'api/tokens/refresh' }); var RefreshTokenCollection = Backbone.Collection.extend({ idAttribute: 'id', - + model: RefreshTokenModel, - + url: 'api/tokens/refresh' - + }); var RefreshTokenView = Backbone.View.extend({ - + tagName: 'tr', - - initialize:function (options) { - this.options = options; - - if (!this.template) { - this.template = _.template($('#tmpl-refresh-token').html()); - } - - if (!this.scopeTemplate) { - this.scopeTemplate = _.template($('#tmpl-scope-list').html()); - } - - if (!this.moreInfoTemplate) { - this.moreInfoTemplate = _.template($('#tmpl-client-more-info-block').html()); - } - - this.model.bind('change', this.render, this); - - }, - - events: { - 'click .btn-delete':'deleteToken', - 'click .token-substring':'showTokenValue', + + initialize: function(options) { + this.options = options; + + if (!this.template) { + this.template = _.template($('#tmpl-refresh-token').html()); + } + + if (!this.scopeTemplate) { + this.scopeTemplate = _.template($('#tmpl-scope-list').html()); + } + + if (!this.moreInfoTemplate) { + this.moreInfoTemplate = _.template($('#tmpl-client-more-info-block').html()); + } + + this.model.bind('change', this.render, this); + + }, + + events: { + 'click .btn-delete': 'deleteToken', + 'click .token-substring': 'showTokenValue', 'click .toggleMoreInformation': 'toggleMoreInformation' }, - - render:function (eventName) { - + + render: function(eventName) { + var expirationDate = this.model.get("expiration"); if (expirationDate == null) { @@ -217,53 +228,65 @@ var RefreshTokenView = Backbone.View.extend({ } else { expirationDate = moment(expirationDate).calendar(); } - - var json = {token: this.model.toJSON(), client: this.options.client.toJSON(), formattedExpiration: expirationDate, accessTokenCount: this.options.accessTokenCount}; + + var json = { + token: this.model.toJSON(), + client: this.options.client.toJSON(), + formattedExpiration: expirationDate, + accessTokenCount: this.options.accessTokenCount + }; this.$el.html(this.template(json)); // hide full value - $('.token-full', this.el).hide(); - + $('.token-full', this.el).hide(); + // show scopes - $('.scope-list', this.el).html(this.scopeTemplate({scopes: this.model.get('scopes'), systemScopes: this.options.systemScopeList})); - - $('.client-more-info-block', this.el).html(this.moreInfoTemplate({client: this.options.client.toJSON()})); - - $(this.el).i18n(); - return this; - - }, - - deleteToken:function (e) { - e.preventDefault(); - - if (confirm($.t('token.token-table.confirm-refresh'))) { - - var _self = this; - - this.model.destroy({ - dataType: false, processData: false, - success:function () { - - _self.$el.fadeTo("fast", 0.00, function () { //fade - $(this).slideUp("fast", function () { //slide up - $(this).remove(); //then remove from the DOM - // refresh the table in case the access tokens have changed, too - _self.parentView.refreshTable(); - }); - }); - }, - error:app.errorHandlerView.handleError() - }); - - _self.parentView.delegateEvents(); - } - - return false; - }, - - toggleMoreInformation:function(e) { + $('.scope-list', this.el).html(this.scopeTemplate({ + scopes: this.model.get('scopes'), + systemScopes: this.options.systemScopeList + })); + + $('.client-more-info-block', this.el).html(this.moreInfoTemplate({ + client: this.options.client.toJSON() + })); + + $(this.el).i18n(); + return this; + + }, + + deleteToken: function(e) { + e.preventDefault(); + + if (confirm($.t('token.token-table.confirm-refresh'))) { + + var _self = this; + + this.model.destroy({ + dataType: false, + processData: false, + success: function() { + + _self.$el.fadeTo("fast", 0.00, function() { // fade + $(this).slideUp("fast", function() { // slide up + $(this).remove(); // then remove from the DOM + // refresh the table in case the access tokens have + // changed, too + _self.parentView.refreshTable(); + }); + }); + }, + error: app.errorHandlerView.handleError() + }); + + _self.parentView.delegateEvents(); + } + + return false; + }, + + toggleMoreInformation: function(e) { e.preventDefault(); if ($('.moreInformation', this.el).is(':visible')) { // hide it @@ -279,101 +302,128 @@ var RefreshTokenView = Backbone.View.extend({ }, - close:function () { - $(this.el).unbind(); - $(this.el).empty(); - }, - - showTokenValue:function (e) { - e.preventDefault(); - $('.token-substring', this.el).hide(); - $('.token-full', this.el).show(); - } + close: function() { + $(this.el).unbind(); + $(this.el).empty(); + }, + + showTokenValue: function(e) { + e.preventDefault(); + $('.token-substring', this.el).hide(); + $('.token-full', this.el).show(); + } }); var TokenListView = Backbone.View.extend({ tagName: 'span', - - initialize:function(options) { - this.options = options; + + initialize: function(options) { + this.options = options; }, - - events:{ - "click .refresh-table":"refreshTable", - 'page .paginator-access':'changePageAccess', - 'page .paginator-refresh':'changePageRefresh' + + events: { + "click .refresh-table": "refreshTable", + 'page .paginator-access': 'changePageAccess', + 'page .paginator-refresh': 'changePageRefresh' }, - load:function(callback) { - if (this.model.access.isFetched && - this.model.refresh.isFetched && - this.options.clientList.isFetched && - this.options.systemScopeList.isFetched) { - callback(); - return; - } - - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t('token.token-table.access-tokens') + ' ' + - '' + $.t('token.token-table.refresh-tokens') + ' ' + - '' + $.t('common.clients') + ' ' + - '' + $.t('common.scopes') + ' ' - ); - - $.when(this.model.access.fetchIfNeeded({success:function(e) {$('#loading-access').addClass('label-success');}, error: app.errorHandlerView.handleError()}), - this.model.refresh.fetchIfNeeded({success:function(e) {$('#loading-refresh').addClass('label-success');}, error: app.errorHandlerView.handleError()}), - this.options.clientList.fetchIfNeeded({success:function(e) {$('#loading-clients').addClass('label-success');}, error: app.errorHandlerView.handleError()}), - this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}, error: app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - callback(); - }); - - }, - - changePageAccess:function(event, num) { - $('.paginator-access', this.el).bootpag({page: num}); + load: function(callback) { + if (this.model.access.isFetched && this.model.refresh.isFetched && this.options.clientList.isFetched && this.options.systemScopeList.isFetched) { + callback(); + return; + } + + $('#loadingbox').sheet('show'); + $('#loading').html( + '' + $.t('token.token-table.access-tokens') + ' ' + '' + $.t('token.token-table.refresh-tokens') + ' ' + '' + $.t('common.clients') + ' ' + '' + + $.t('common.scopes') + ' '); + + $.when(this.model.access.fetchIfNeeded({ + success: function(e) { + $('#loading-access').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.model.refresh.fetchIfNeeded({ + success: function(e) { + $('#loading-refresh').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.clientList.fetchIfNeeded({ + success: function(e) { + $('#loading-clients').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.systemScopeList.fetchIfNeeded({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + callback(); + }); + + }, + + changePageAccess: function(event, num) { + $('.paginator-access', this.el).bootpag({ + page: num + }); $('#access-token-table tbody tr', this.el).each(function(index, element) { if (Math.ceil((index + 1) / 10) != num) { - $(element).hide(); - } else { - $(element).show(); - } + $(element).hide(); + } else { + $(element).show(); + } }); }, - - changePageRefresh:function(event, num) { - $('.paginator-refresh', this.el).bootpag({page: num}); + + changePageRefresh: function(event, num) { + $('.paginator-refresh', this.el).bootpag({ + page: num + }); $('#refresh-token-table tbody tr', this.el).each(function(index, element) { if (Math.ceil((index + 1) / 10) != num) { - $(element).hide(); - } else { - $(element).show(); - } + $(element).hide(); + } else { + $(element).show(); + } }); }, - - refreshTable:function(e) { - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t('token.token-table.access-tokens') + ' ' + - '' + $.t('token.token-table.refresh-tokens') + ' ' + - '' + $.t('common.clients') + ' ' + - '' + $.t('common.scopes') + ' ' - ); - var _self = this; - $.when(this.model.access.fetch({success:function(e) {$('#loading-access').addClass('label-success');}, error: app.errorHandlerView.handleError()}), - this.model.refresh.fetch({success:function(e) {$('#loading-refresh').addClass('label-success');}, error: app.errorHandlerView.handleError()}), - this.options.clientList.fetch({success:function(e) {$('#loading-clients').addClass('label-success');}, error: app.errorHandlerView.handleError()}), - this.options.systemScopeList.fetch({success:function(e) {$('#loading-scopes').addClass('label-success');}, error: app.errorHandlerView.handleError()})) - .done(function(){ - _self.render(); - $('#loadingbox').sheet('hide'); - }); + + refreshTable: function(e) { + $('#loadingbox').sheet('show'); + $('#loading').html( + '' + $.t('token.token-table.access-tokens') + ' ' + '' + $.t('token.token-table.refresh-tokens') + ' ' + '' + $.t('common.clients') + ' ' + '' + + $.t('common.scopes') + ' '); + var _self = this; + $.when(this.model.access.fetch({ + success: function(e) { + $('#loading-access').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.model.refresh.fetch({ + success: function(e) { + $('#loading-refresh').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.clientList.fetch({ + success: function(e) { + $('#loading-clients').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.systemScopeList.fetch({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + _self.render(); + $('#loadingbox').sheet('hide'); + }); }, - - togglePlaceholder:function() { + + togglePlaceholder: function() { if (this.model.access.length > 0) { $('#access-token-table', this.el).show(); $('#access-token-table-empty', this.el).hide(); @@ -388,122 +438,140 @@ var TokenListView = Backbone.View.extend({ $('#refresh-token-table', this.el).hide(); $('#refresh-token-table-empty', this.el).show(); } - + $('#access-token-count', this.el).html(this.model.access.length); $('#refresh-token-count', this.el).html(this.model.refresh.length); }, - - render: function (eventName) { - + + render: function(eventName) { + // append and render the table structure $(this.el).html($('#tmpl-token-table').html()); - + var _self = this; - - // set up pagination - var numPagesAccess = Math.ceil(this.model.access.length / 10); - if (numPagesAccess > 1) { - $('.paginator-access', this.el).show(); - $('.paginator-access', this.el).bootpag({ - total: numPagesAccess, - page: 1 - }); - } else { - $('.paginator-access', this.el).hide(); - } - - // count up refresh tokens - var refreshCount = {}; - - _.each(this.model.access.models, function (token, index) { + + // set up pagination + var numPagesAccess = Math.ceil(this.model.access.length / 10); + if (numPagesAccess > 1) { + $('.paginator-access', this.el).show(); + $('.paginator-access', this.el).bootpag({ + total: numPagesAccess, + page: 1 + }); + } else { + $('.paginator-access', this.el).hide(); + } + + // count up refresh tokens + var refreshCount = {}; + + _.each(this.model.access.models, function(token, index) { // look up client var client = _self.options.clientList.getByClientId(token.get('clientId')); - var view = new AccessTokenView({model: token, client: client, systemScopeList: _self.options.systemScopeList}); + var view = new AccessTokenView({ + model: token, + client: client, + systemScopeList: _self.options.systemScopeList + }); view.parentView = _self; var element = view.render().el; $('#access-token-table', _self.el).append(element); - if (Math.ceil((index + 1) / 10) != 1) { - $(element).hide(); - } - - //console.log(token.get('refreshTokenId')); - var refId = token.get('refreshTokenId'); - if (refId != null) { - if (refreshCount[refId]) { - refreshCount[refId] += 1; - } else { - refreshCount[refId] = 1; - } - - } + if (Math.ceil((index + 1) / 10) != 1) { + $(element).hide(); + } + + // console.log(token.get('refreshTokenId')); + var refId = token.get('refreshTokenId'); + if (refId != null) { + if (refreshCount[refId]) { + refreshCount[refId] += 1; + } else { + refreshCount[refId] = 1; + } + + } }); - //console.log(refreshCount); - - // set up pagination - var numPagesRefresh = Math.ceil(this.model.refresh.length / 10); - if (numPagesRefresh > 1) { - $('.paginator-refresh', this.el).show(); - $('.paginator-refresh', this.el).bootpag({ - total: numPagesRefresh, - page: 1 - }); - } else { - $('.paginator-refresh', this.el).hide(); - } - - _.each(this.model.refresh.models, function (token, index) { + // console.log(refreshCount); + + // set up pagination + var numPagesRefresh = Math.ceil(this.model.refresh.length / 10); + if (numPagesRefresh > 1) { + $('.paginator-refresh', this.el).show(); + $('.paginator-refresh', this.el).bootpag({ + total: numPagesRefresh, + page: 1 + }); + } else { + $('.paginator-refresh', this.el).hide(); + } + + _.each(this.model.refresh.models, function(token, index) { // look up client var client = _self.options.clientList.getByClientId(token.get('clientId')); - var view = new RefreshTokenView({model: token, client: client, systemScopeList: _self.options.systemScopeList, accessTokenCount: refreshCount[token.get('id')]}); + var view = new RefreshTokenView({ + model: token, + client: client, + systemScopeList: _self.options.systemScopeList, + accessTokenCount: refreshCount[token.get('id')] + }); view.parentView = _self; var element = view.render().el; $('#refresh-token-table', _self.el).append(element); - if (Math.ceil((index + 1) / 10) != 1) { - $(element).hide(); - } + if (Math.ceil((index + 1) / 10) != 1) { + $(element).hide(); + } }); - -/* - _.each(this.model.models, function (scope) { - $("#scope-table", this.el).append(new SystemScopeView({model: scope}).render().el); - }, this); -*/ - + + /* + * _.each(this.model.models, function (scope) { $("#scope-table", + * this.el).append(new SystemScopeView({model: scope}).render().el); }, + * this); + */ + this.togglePlaceholder(); - $(this.el).i18n(); + $(this.el).i18n(); return this; } }); - -ui.routes.push({path: "user/tokens", name: "tokens", callback: - function() { +ui.routes.push({ + path: "user/tokens", + name: "tokens", + callback: function() { this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('token.manage'), href:"manage/#user/tokens"} - ]); - - this.updateSidebar('user/tokens'); - - var view = new TokenListView({model: {access: this.accessTokensList, refresh: this.refreshTokensList}, clientList: this.clientList, systemScopeList: this.systemScopeList}); - - view.load( - function(collection, response, options) { - $('#content').html(view.render().el); - setPageTitle($.t('token.manage')); - } - ); - + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('token.manage'), + href: "manage/#user/tokens" + }]); + + this.updateSidebar('user/tokens'); + + var view = new TokenListView({ + model: { + access: this.accessTokensList, + refresh: this.refreshTokensList + }, + clientList: this.clientList, + systemScopeList: this.systemScopeList + }); + + view.load(function(collection, response, options) { + $('#content').html(view.render().el); + setPageTitle($.t('token.manage')); + }); + } }); ui.templates.push('resources/template/token.html'); ui.init.push(function(app) { - app.accessTokensList = new AccessTokenCollection(); - app.refreshTokensList = new RefreshTokenCollection(); + app.accessTokensList = new AccessTokenCollection(); + app.refreshTokensList = new RefreshTokenCollection(); }); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js b/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js index 8441bdebdd..293bf669e3 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/whitelist.js @@ -15,94 +15,107 @@ * limitations under the License. *******************************************************************************/ var WhiteListModel = Backbone.Model.extend({ - + idAttribute: "id", - - initialize: function () { }, - + + initialize: function() { + }, + urlRoot: "api/whitelist" - + }); var WhiteListCollection = Backbone.Collection.extend({ initialize: function() { - //this.fetch(); + // this.fetch(); }, - - getByClientId: function(clientId) { - var clients = this.where({clientId: clientId}); + + getByClientId: function(clientId) { + var clients = this.where({ + clientId: clientId + }); if (clients.length == 1) { return clients[0]; } else { return null; } - }, - + }, + model: WhiteListModel, url: "api/whitelist" - + }); var WhiteListListView = Backbone.View.extend({ tagName: 'span', - - initialize:function (options) { - this.options = options; + + initialize: function(options) { + this.options = options; }, - load:function(callback) { - if (this.model.isFetched && - this.options.clientList.isFetched && - this.options.systemScopeList.isFetched) { - callback(); - return; - } - - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t('whitelist.whitelist') + ' ' + - '' + $.t('common.clients') + ' ' + - '' + $.t('common.scopes') + ' ' - ); - - $.when(this.model.fetchIfNeeded({success:function(e) {$('#loading-whitelist').addClass('label-success');}, error: app.errorHandlerView.handleError()}), - this.options.clientList.fetchIfNeeded({success:function(e) {$('#loading-clients').addClass('label-success');}, error: app.errorHandlerView.handleError()}), - this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}, error: app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - callback(); - }); - }, - - events:{ - "click .refresh-table":"refreshTable" + load: function(callback) { + if (this.model.isFetched && this.options.clientList.isFetched && this.options.systemScopeList.isFetched) { + callback(); + return; + } + + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t('whitelist.whitelist') + ' ' + '' + $.t('common.clients') + ' ' + '' + $.t('common.scopes') + ' '); + + $.when(this.model.fetchIfNeeded({ + success: function(e) { + $('#loading-whitelist').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.clientList.fetchIfNeeded({ + success: function(e) { + $('#loading-clients').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.systemScopeList.fetchIfNeeded({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + callback(); + }); }, - - render:function (eventName) { + + events: { + "click .refresh-table": "refreshTable" + }, + + render: function(eventName) { $(this.el).html($('#tmpl-whitelist-table').html()); - + var _self = this; - - _.each(this.model.models, function (whiteList) { - + + _.each(this.model.models, function(whiteList) { + // look up client var client = _self.options.clientList.getByClientId(whiteList.get('clientId')); - + // if there's no client ID, this is an error! if (client != null) { - var view = new WhiteListView({model: whiteList, client: client, systemScopeList: _self.options.systemScopeList}); + var view = new WhiteListView({ + model: whiteList, + client: client, + systemScopeList: _self.options.systemScopeList + }); view.parentView = _self; $('#whitelist-table', _self.el).append(view.render().el); } - + }, this); this.togglePlaceholder(); - $(this.el).i18n(); + $(this.el).i18n(); return this; }, - togglePlaceholder:function() { + togglePlaceholder: function() { if (this.model.length > 0) { $('#whitelist-table', this.el).show(); $('#whitelist-table-empty', this.el).hide(); @@ -111,108 +124,129 @@ var WhiteListListView = Backbone.View.extend({ $('#whitelist-table-empty', this.el).show(); } }, - - refreshTable:function(e) { - e.preventDefault(); - var _self = this; - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t('whitelist.whitelist') + ' ' + - '' + $.t('common.clients') + ' ' + - '' + $.t('common.scopes') + ' ' - ); - - $.when(this.model.fetch({success:function(e) {$('#loading-whitelist').addClass('label-success');}, error: app.errorHandlerView.handleError()}), - this.options.clientList.fetch({success:function(e) {$('#loading-clients').addClass('label-success');}, error: app.errorHandlerView.handleError()}), - this.options.systemScopeList.fetch({success:function(e) {$('#loading-scopes').addClass('label-success');}, error: app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - _self.render(); - }); - } + + refreshTable: function(e) { + e.preventDefault(); + var _self = this; + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t('whitelist.whitelist') + ' ' + '' + $.t('common.clients') + ' ' + '' + $.t('common.scopes') + ' '); + + $.when(this.model.fetch({ + success: function(e) { + $('#loading-whitelist').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.clientList.fetch({ + success: function(e) { + $('#loading-clients').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.systemScopeList.fetch({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + _self.render(); + }); + } }); var WhiteListView = Backbone.View.extend({ tagName: 'tr', - - initialize:function(options) { - this.options = options; + + initialize: function(options) { + this.options = options; if (!this.template) { this.template = _.template($('#tmpl-whitelist').html()); } - - if (!this.scopeTemplate) { - this.scopeTemplate = _.template($('#tmpl-scope-list').html()); - } - if (!this.moreInfoTemplate) { - this.moreInfoTemplate = _.template($('#tmpl-client-more-info-block').html()); - } + if (!this.scopeTemplate) { + this.scopeTemplate = _.template($('#tmpl-scope-list').html()); + } + + if (!this.moreInfoTemplate) { + this.moreInfoTemplate = _.template($('#tmpl-client-more-info-block').html()); + } this.model.bind('change', this.render, this); }, - - render:function(eventName) { - - var json = {whiteList: this.model.toJSON(), client: this.options.client.toJSON()}; - + + render: function(eventName) { + + var json = { + whiteList: this.model.toJSON(), + client: this.options.client.toJSON() + }; + this.$el.html(this.template(json)); - $('.scope-list', this.el).html(this.scopeTemplate({scopes: this.model.get('allowedScopes'), systemScopes: this.options.systemScopeList})); - - $('.client-more-info-block', this.el).html(this.moreInfoTemplate({client: this.options.client.toJSON()})); - - this.$('.dynamically-registered').tooltip({title: $.t('common.dynamically-registered')}); + $('.scope-list', this.el).html(this.scopeTemplate({ + scopes: this.model.get('allowedScopes'), + systemScopes: this.options.systemScopeList + })); + + $('.client-more-info-block', this.el).html(this.moreInfoTemplate({ + client: this.options.client.toJSON() + })); + + this.$('.dynamically-registered').tooltip({ + title: $.t('common.dynamically-registered') + }); - $(this.el).i18n(); - return this; + $(this.el).i18n(); + return this; }, - - events:{ + + events: { 'click .btn-edit': 'editWhitelist', 'click .btn-delete': 'deleteWhitelist', 'click .toggleMoreInformation': 'toggleMoreInformation' }, - - editWhitelist:function(e) { - e.preventDefault(); - app.navigate('admin/whitelist/' + this.model.get('id'), {trigger: true}); + + editWhitelist: function(e) { + e.preventDefault(); + app.navigate('admin/whitelist/' + this.model.get('id'), { + trigger: true + }); }, - - deleteWhitelist:function(e) { - e.preventDefault(); - + + deleteWhitelist: function(e) { + e.preventDefault(); + if (confirm($.t('whitelist.confirm'))) { var _self = this; - - this.model.destroy({ - dataType: false, processData: false, - success:function () { - _self.$el.fadeTo("fast", 0.00, function () { //fade - $(this).slideUp("fast", function () { //slide up - $(this).remove(); //then remove from the DOM - // check the placeholder in case it's empty now - _self.parentView.togglePlaceholder(); - }); - }); - }, - error:app.errorHandlerView.handleError() - }); - - _self.parentView.delegateEvents(); + + this.model.destroy({ + dataType: false, + processData: false, + success: function() { + _self.$el.fadeTo("fast", 0.00, function() { // fade + $(this).slideUp("fast", function() { // slide up + $(this).remove(); // then remove from the DOM + // check the placeholder in case it's empty now + _self.parentView.togglePlaceholder(); + }); + }); + }, + error: app.errorHandlerView.handleError() + }); + + _self.parentView.delegateEvents(); } - + return false; }, - - toggleMoreInformation:function(e) { + + toggleMoreInformation: function(e) { e.preventDefault(); if ($('.moreInformation', this.el).is(':visible')) { // hide it $('.moreInformation', this.el).hide('fast'); $('.toggleMoreInformation i', this.el).attr('class', 'icon-chevron-right'); $('.moreInformationContainer', this.el).removeClass('alert').removeClass('alert-info').addClass('muted'); - + } else { // show it $('.moreInformation', this.el).show('fast'); @@ -221,7 +255,7 @@ var WhiteListView = Backbone.View.extend({ } }, - close:function() { + close: function() { $(this.el).unbind(); $(this.el).empty(); } @@ -229,278 +263,333 @@ var WhiteListView = Backbone.View.extend({ var WhiteListFormView = Backbone.View.extend({ tagName: 'span', - - initialize:function (options) { - this.options = options; + + initialize: function(options) { + this.options = options; if (!this.template) { this.template = _.template($('#tmpl-whitelist-form').html()); } - + this.scopeCollection = new Backbone.Collection(); this.listWidgetViews = []; - + }, - load:function(callback) { + load: function(callback) { if (this.options.client) { // we know what client we're dealing with already - if (this.model.isFetched && - this.options.client.isFetched) { - callback(); - return; - } - - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t('whitelist.whitelist') + ' ' + - '' + $.t('common.clients') + ' ' + - '' + $.t('common.scopes') + ' ' - ); - - $.when(this.model.fetchIfNeeded({success:function(e) {$('#loading-whitelist').addClass('label-success');}, error: app.errorHandlerView.handleError()}), - this.options.client.fetchIfNeeded({success:function(e) {$('#loading-clients').addClass('label-success');}, error: app.errorHandlerView.handleError()}), - this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}, error: app.errorHandlerView.handleError()})) - .done(function() { - $('#loadingbox').sheet('hide'); - callback(); - }); - + if (this.model.isFetched && this.options.client.isFetched) { + callback(); + return; + } + + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t('whitelist.whitelist') + ' ' + '' + $.t('common.clients') + ' ' + '' + $.t('common.scopes') + ' '); + + $.when(this.model.fetchIfNeeded({ + success: function(e) { + $('#loading-whitelist').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.client.fetchIfNeeded({ + success: function(e) { + $('#loading-clients').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.systemScopeList.fetchIfNeeded({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + $('#loadingbox').sheet('hide'); + callback(); + }); + } else { // we need to get the client information from the list - - if (this.model.isFetched && - this.options.clientList.isFetched && - this.options.systemScopeList.isFetched) { - + + if (this.model.isFetched && this.options.clientList.isFetched && this.options.systemScopeList.isFetched) { + var client = this.options.clientList.getByClientId(this.model.get('clientId')); this.options.client = client; - - callback(); - return; - } - - $('#loadingbox').sheet('show'); - $('#loading').html( - '' + $.t('whitelist.whitelist') + ' ' + - '' + $.t('common.clients') + ' ' + - '' + $.t('common.scopes') + ' ' - ); - - var _self = this; - - $.when(this.model.fetchIfNeeded({success:function(e) {$('#loading-whitelist').addClass('label-success');}, error: app.errorHandlerView.handleError()}), - this.options.clientList.fetchIfNeeded({success:function(e) {$('#loading-clients').addClass('label-success');}, error: app.errorHandlerView.handleError()}), - this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}, error: app.errorHandlerView.handleError()})) - .done(function() { - - var client = _self.options.clientList.getByClientId(_self.model.get('clientId')); - _self.options.client = client; - - $('#loadingbox').sheet('hide'); - callback(); - }); - + + callback(); + return; + } + + $('#loadingbox').sheet('show'); + $('#loading').html('' + $.t('whitelist.whitelist') + ' ' + '' + $.t('common.clients') + ' ' + '' + $.t('common.scopes') + ' '); + + var _self = this; + + $.when(this.model.fetchIfNeeded({ + success: function(e) { + $('#loading-whitelist').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.clientList.fetchIfNeeded({ + success: function(e) { + $('#loading-clients').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + }), this.options.systemScopeList.fetchIfNeeded({ + success: function(e) { + $('#loading-scopes').addClass('label-success'); + }, + error: app.errorHandlerView.handleError() + })).done(function() { + + var client = _self.options.clientList.getByClientId(_self.model.get('clientId')); + _self.options.client = client; + + $('#loadingbox').sheet('hide'); + callback(); + }); + } - - - }, - - events:{ - 'click .btn-save':'saveWhiteList', - 'click .btn-cancel':'cancelWhiteList', - + + events: { + 'click .btn-save': 'saveWhiteList', + 'click .btn-cancel': 'cancelWhiteList', + }, - - saveWhiteList:function (e) { - e.preventDefault(); + + saveWhiteList: function(e) { + e.preventDefault(); $('.control-group').removeClass('error'); - - // sync any leftover collection items - _.each(this.listWidgetViews, function(v) { - v.addItem($.Event('click')); - }); - + + // sync any leftover collection items + _.each(this.listWidgetViews, function(v) { + v.addItem($.Event('click')); + }); + // process allowed scopes - var allowedScopes = this.scopeCollection.pluck("item"); - - this.model.set({clientId: this.options.client.get('clientId')}, {silent: true}); - + var allowedScopes = this.scopeCollection.pluck("item"); + + this.model.set({ + clientId: this.options.client.get('clientId') + }, { + silent: true + }); + var valid = this.model.set({ allowedScopes: allowedScopes }); - - if (valid) { - var _self = this; - this.model.save({}, { - success:function () { - app.whiteListList.add(_self.model); - app.navigate('admin/whitelists', {trigger:true}); - }, - error:app.errorHandlerView.handleError() - }); - } - - return false; - + + if (valid) { + var _self = this; + this.model.save({}, { + success: function() { + app.whiteListList.add(_self.model); + app.navigate('admin/whitelists', { + trigger: true + }); + }, + error: app.errorHandlerView.handleError() + }); + } + + return false; + }, - - cancelWhiteList:function(e) { - e.preventDefault(); - // TODO: figure out where we came from and go back there instead - if (this.model.get('id') == null) { - // if it's a new whitelist entry, go back to the client listing page - app.navigate('admin/clients', {trigger:true}); - } else { - // if we're editing a whitelist, go back to the whitelists page - app.navigate('admin/whitelists', {trigger:true}); - } + + cancelWhiteList: function(e) { + e.preventDefault(); + // TODO: figure out where we came from and go back there instead + if (this.model.get('id') == null) { + // if it's a new whitelist entry, go back to the client listing page + app.navigate('admin/clients', { + trigger: true + }); + } else { + // if we're editing a whitelist, go back to the whitelists page + app.navigate('admin/whitelists', { + trigger: true + }); + } }, - - render:function (eventName) { - - var json = {whiteList: this.model.toJSON(), client: this.options.client.toJSON()}; - + + render: function(eventName) { + + var json = { + whiteList: this.model.toJSON(), + client: this.options.client.toJSON() + }; + this.$el.html(this.template(json)); - + this.listWidgetViews = []; - - var _self = this; - // build and bind scopes - _.each(this.model.get("allowedScopes"), function (scope) { - _self.scopeCollection.add(new Backbone.Model({item:scope})); - }); - - var scopeView = new ListWidgetView({ - placeholder: $.t('whitelist.whitelist-form.scope-placeholder'), - autocomplete: this.options.client.get("scope"), - helpBlockText: $.t('whitelist.whitelist-form.scope-help'), - collection: this.scopeCollection}); - $("#scope .controls",this.el).html(scopeView.render().el); - this.listWidgetViews.push(scopeView); - - $(this.el).i18n(); + + var _self = this; + // build and bind scopes + _.each(this.model.get("allowedScopes"), function(scope) { + _self.scopeCollection.add(new Backbone.Model({ + item: scope + })); + }); + + var scopeView = new ListWidgetView({ + placeholder: $.t('whitelist.whitelist-form.scope-placeholder'), + autocomplete: this.options.client.get("scope"), + helpBlockText: $.t('whitelist.whitelist-form.scope-help'), + collection: this.scopeCollection + }); + $("#scope .controls", this.el).html(scopeView.render().el); + this.listWidgetViews.push(scopeView); + + $(this.el).i18n(); return this; } }); - -ui.routes.push({path: "admin/whitelists", name: "whiteList", callback: - function () { +ui.routes.push({ + path: "admin/whitelists", + name: "whiteList", + callback: function() { if (!isAdmin()) { this.root(); return; } - - this.updateSidebar('admin/whitelists'); - - this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('whitelist.manage'), href:"manage/#admin/whitelists"} - ]); - - var view = new WhiteListListView({model:this.whiteListList, clientList: this.clientList, systemScopeList: this.systemScopeList}); - - view.load( - function() { - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('whitelist.manage')); - } - ); - - + + this.updateSidebar('admin/whitelists'); + + this.breadCrumbView.collection.reset(); + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('whitelist.manage'), + href: "manage/#admin/whitelists" + }]); + + var view = new WhiteListListView({ + model: this.whiteListList, + clientList: this.clientList, + systemScopeList: this.systemScopeList + }); + + view.load(function() { + $('#content').html(view.render().el); + view.delegateEvents(); + setPageTitle($.t('whitelist.manage')); + }); + } }); -ui.routes.push({path: "admin/whitelist/new/:cid", name: "newWhitelist", callback: - function(cid) { - +ui.routes.push({ + path: "admin/whitelist/new/:cid", + name: "newWhitelist", + callback: function(cid) { if (!isAdmin()) { this.root(); return; } - + this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('whitelist.manage'), href:"manage/#admin/whitelists"}, - {text:$.t('whitelist.new'), href:"manage/#admin/whitelist/new/" + cid} - ]); - - this.updateSidebar('admin/whitelists'); - - var whiteList = new WhiteListModel(); - - var client = this.clientList.get(cid); - if (!client) { - client = new ClientModel({id: cid}); - } - - var view = new WhiteListFormView({model: whiteList, client: client, systemScopeList: this.systemScopeList}); - - view.load( - function() { - - // set the scopes on the model now that everything's loaded - whiteList.set({allowedScopes: client.get('scope')}, {silent: true}); - - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('whitelist.manage')); - } - ); - + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('whitelist.manage'), + href: "manage/#admin/whitelists" + }, { + text: $.t('whitelist.new'), + href: "manage/#admin/whitelist/new/" + cid + }]); + + this.updateSidebar('admin/whitelists'); + + var whiteList = new WhiteListModel(); + + var client = this.clientList.get(cid); + if (!client) { + client = new ClientModel({ + id: cid + }); + } + + var view = new WhiteListFormView({ + model: whiteList, + client: client, + systemScopeList: this.systemScopeList + }); + + view.load(function() { + + // set the scopes on the model now that everything's loaded + whiteList.set({ + allowedScopes: client.get('scope') + }, { + silent: true + }); + + $('#content').html(view.render().el); + view.delegateEvents(); + setPageTitle($.t('whitelist.manage')); + }); + } }); - -ui.routes.push({path: "admin/whitelist/:id", name: "editWhitelist", callback: - function(id) { +ui.routes.push({ + path: "admin/whitelist/:id", + name: "editWhitelist", + callback: function(id) { if (!isAdmin()) { this.root(); return; } - + this.breadCrumbView.collection.reset(); - this.breadCrumbView.collection.add([ - {text:$.t('admin.home'), href:""}, - {text:$.t('whitelist.manage'), href:"manage/#admin/whitelists"}, - {text:$.t('whitelist.edit'), href:"manage/#admin/whitelist/" + id} - ]); - - this.updateSidebar('admin/whitelists'); - - var whiteList = this.whiteListList.get(id); - if (!whiteList) { - whiteList = new WhiteListModel({id: id}); - } - - var view = new WhiteListFormView({model: whiteList, clientList: this.clientList, systemScopeList: this.systemScopeList}); - - view.load( - function() { - $('#content').html(view.render().el); - view.delegateEvents(); - setPageTitle($.t('whitelist.manage')); - } - ); - + this.breadCrumbView.collection.add([{ + text: $.t('admin.home'), + href: "" + }, { + text: $.t('whitelist.manage'), + href: "manage/#admin/whitelists" + }, { + text: $.t('whitelist.edit'), + href: "manage/#admin/whitelist/" + id + }]); + + this.updateSidebar('admin/whitelists'); + + var whiteList = this.whiteListList.get(id); + if (!whiteList) { + whiteList = new WhiteListModel({ + id: id + }); + } + + var view = new WhiteListFormView({ + model: whiteList, + clientList: this.clientList, + systemScopeList: this.systemScopeList + }); + + view.load(function() { + $('#content').html(view.render().el); + view.delegateEvents(); + setPageTitle($.t('whitelist.manage')); + }); + } - + }); ui.templates.push('resources/template/whitelist.html'); ui.init.push(function(app) { - app.whiteListList = new WhiteListCollection(); + app.whiteListList = new WhiteListCollection(); }); From 7a225e56c4d788857b4137c115ea3f3064fcd869 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 22 Mar 2017 17:29:37 -0400 Subject: [PATCH 174/269] =?UTF-8?q?don=E2=80=99t=20pack=20wro4j=20into=20.?= =?UTF-8?q?war=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use it only during compilation, not runtime --- openid-connect-server-webapp/pom.xml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index 528201c049..b8b9d5beea 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -94,15 +94,18 @@ ${project.build.directory}/${project.build.finalName}/js/ ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory + + + ro.isdc.wro4j + wro4j-extensions + 1.8.0 + +
    - - ro.isdc.wro4j - wro4j-extensions - org.mitre openid-connect-server From 7767c7a831d40d0138e8c66c983448721c941b74 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 22 Mar 2017 17:30:08 -0400 Subject: [PATCH 175/269] =?UTF-8?q?add=20=E2=80=9Cif=20not=20exists?= =?UTF-8?q?=E2=80=9D=20to=20all=20indexes=20in=20HSQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../db/hsql/hsql_database_tables.sql | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql index 00851a775e..4274e53566 100644 --- a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql @@ -383,17 +383,17 @@ CREATE TABLE IF NOT EXISTS device_code_request_parameter ( val VARCHAR(2048) ); -CREATE INDEX at_tv_idx ON access_token(token_value); -CREATE INDEX ts_oi_idx ON token_scope(owner_id); -CREATE INDEX at_exp_idx ON access_token(expiration); -CREATE INDEX rf_ahi_idx ON refresh_token(auth_holder_id); -CREATE INDEX cd_ci_idx ON client_details(client_id); -CREATE INDEX at_ahi_idx ON access_token(auth_holder_id); -CREATE INDEX aha_oi_idx ON authentication_holder_authority(owner_id); -CREATE INDEX ahe_oi_idx ON authentication_holder_extension(owner_id); -CREATE INDEX ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); -CREATE INDEX ahri_oi_idx ON authentication_holder_resource_id(owner_id); -CREATE INDEX ahrt_oi_idx ON authentication_holder_response_type(owner_id); -CREATE INDEX ahs_oi_idx ON authentication_holder_scope(owner_id); -CREATE INDEX ac_ahi_idx ON authorization_code(auth_holder_id); -CREATE INDEX suaa_oi_idx ON saved_user_auth_authority(owner_id); +CREATE INDEX IF NOT EXISTS at_tv_idx ON access_token(token_value); +CREATE INDEX IF NOT EXISTS ts_oi_idx ON token_scope(owner_id); +CREATE INDEX IF NOT EXISTS at_exp_idx ON access_token(expiration); +CREATE INDEX IF NOT EXISTS rf_ahi_idx ON refresh_token(auth_holder_id); +CREATE INDEX IF NOT EXISTS cd_ci_idx ON client_details(client_id); +CREATE INDEX IF NOT EXISTS at_ahi_idx ON access_token(auth_holder_id); +CREATE INDEX IF NOT EXISTS aha_oi_idx ON authentication_holder_authority(owner_id); +CREATE INDEX IF NOT EXISTS ahe_oi_idx ON authentication_holder_extension(owner_id); +CREATE INDEX IF NOT EXISTS ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); +CREATE INDEX IF NOT EXISTS ahri_oi_idx ON authentication_holder_resource_id(owner_id); +CREATE INDEX IF NOT EXISTS ahrt_oi_idx ON authentication_holder_response_type(owner_id); +CREATE INDEX IF NOT EXISTS ahs_oi_idx ON authentication_holder_scope(owner_id); +CREATE INDEX IF NOT EXISTS ac_ahi_idx ON authorization_code(auth_holder_id); +CREATE INDEX IF NOT EXISTS suaa_oi_idx ON saved_user_auth_authority(owner_id); From 050662dd5ceede080f99367d0af707aa63ae2e67 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 24 Mar 2017 12:12:06 -0400 Subject: [PATCH 176/269] updated dependencies --- .../impl/TestSignedAuthRequestUrlBuilder.java | 2 +- .../SymmetricKeyJWTValidatorCacheService.java | 2 +- .../org/mitre/jose/TestJWKSetKeyStore.java | 4 +- ...aultJWTEncryptionAndDecryptionService.java | 8 +- openid-connect-server-webapp/pom.xml | 7 -- pom.xml | 99 +++++++++++-------- uma-server-webapp/pom.xml | 36 +------ 7 files changed, 69 insertions(+), 89 deletions(-) diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java index 8ba354dc7d..ee5ecd71aa 100644 --- a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java +++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java @@ -91,7 +91,7 @@ public class TestSignedAuthRequestUrlBuilder { @Before public void prepare() throws NoSuchAlgorithmException, InvalidKeySpecException { - RSAKey key = new RSAKey(new Base64URL(n), new Base64URL(e), new Base64URL(d), KeyUse.SIGNATURE, null, new Algorithm(alg), kid, null, null, null); + RSAKey key = new RSAKey(new Base64URL(n), new Base64URL(e), new Base64URL(d), KeyUse.SIGNATURE, null, new Algorithm(alg), kid, null, null, null, null); Map keys = Maps.newHashMap(); keys.put("client", key); diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/SymmetricKeyJWTValidatorCacheService.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/SymmetricKeyJWTValidatorCacheService.java index f90a732861..d034ceb6e9 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/SymmetricKeyJWTValidatorCacheService.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/SymmetricKeyJWTValidatorCacheService.java @@ -101,7 +101,7 @@ public JWTSigningAndValidationService load(String key) throws Exception { String id = "SYMMETRIC-KEY"; - JWK jwk = new OctetSequenceKey(Base64URL.encode(key), KeyUse.SIGNATURE, null, null, id, null, null, null); + JWK jwk = new OctetSequenceKey(Base64URL.encode(key), KeyUse.SIGNATURE, null, null, id, null, null, null, null); Map keys = ImmutableMap.of(id, jwk); JWTSigningAndValidationService service = new DefaultJWTSigningAndValidationService(keys); diff --git a/openid-connect-common/src/test/java/org/mitre/jose/TestJWKSetKeyStore.java b/openid-connect-common/src/test/java/org/mitre/jose/TestJWKSetKeyStore.java index d894edddd8..c6efada2bf 100644 --- a/openid-connect-common/src/test/java/org/mitre/jose/TestJWKSetKeyStore.java +++ b/openid-connect-common/src/test/java/org/mitre/jose/TestJWKSetKeyStore.java @@ -62,7 +62,7 @@ public class TestJWKSetKeyStore { "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" + "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" + "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ"), // d - KeyUse.ENCRYPTION, null, JWEAlgorithm.RSA_OAEP, RSAkid, null, null, null); + KeyUse.ENCRYPTION, null, JWEAlgorithm.RSA_OAEP, RSAkid, null, null, null, null); private String RSAkid_rsa2 = "rsa_2"; private JWK RSAjwk_rsa2 = new RSAKey( @@ -79,7 +79,7 @@ public class TestJWKSetKeyStore { "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" + "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" + "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ"), // d - KeyUse.ENCRYPTION, null, JWEAlgorithm.RSA1_5, RSAkid_rsa2, null, null, null); + KeyUse.ENCRYPTION, null, JWEAlgorithm.RSA1_5, RSAkid_rsa2, null, null, null, null); List keys_list = new LinkedList<>(); diff --git a/openid-connect-common/src/test/java/org/mitre/jwt/encryption/service/impl/TestDefaultJWTEncryptionAndDecryptionService.java b/openid-connect-common/src/test/java/org/mitre/jwt/encryption/service/impl/TestDefaultJWTEncryptionAndDecryptionService.java index 06f399f094..a5c851e9e6 100644 --- a/openid-connect-common/src/test/java/org/mitre/jwt/encryption/service/impl/TestDefaultJWTEncryptionAndDecryptionService.java +++ b/openid-connect-common/src/test/java/org/mitre/jwt/encryption/service/impl/TestDefaultJWTEncryptionAndDecryptionService.java @@ -105,7 +105,7 @@ public class TestDefaultJWTEncryptionAndDecryptionService { "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" + "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" + "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ"), // d - KeyUse.ENCRYPTION, null, JWEAlgorithm.RSA_OAEP, RSAkid, null, null, null); + KeyUse.ENCRYPTION, null, JWEAlgorithm.RSA_OAEP, RSAkid, null, null, null, null); private String RSAkid_2 = "rsa3210"; private JWK RSAjwk_2 = new RSAKey( @@ -122,12 +122,12 @@ public class TestDefaultJWTEncryptionAndDecryptionService { "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" + "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" + "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ"), // d - KeyUse.ENCRYPTION, null, JWEAlgorithm.RSA1_5, RSAkid_2, null, null, null); + KeyUse.ENCRYPTION, null, JWEAlgorithm.RSA1_5, RSAkid_2, null, null, null, null); private String AESkid = "aes123"; - private JWK AESjwk = new OctetSequenceKey( new Base64URL("GawgguFyGrWKav7AX4VKUg"), + private JWK AESjwk = new OctetSequenceKey(new Base64URL("GawgguFyGrWKav7AX4VKUg"), KeyUse.ENCRYPTION, null, JWEAlgorithm.A128KW, - AESkid, null, null, null); + AESkid, null, null, null, null); private Map keys = new ImmutableMap.Builder() diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index b8b9d5beea..cb3386dabc 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -94,13 +94,6 @@ ${project.build.directory}/${project.build.finalName}/js/ ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory - - - ro.isdc.wro4j - wro4j-extensions - 1.8.0 - - diff --git a/pom.xml b/pom.xml index a8bbcf5e82..d4901e67b9 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,7 @@ 1.8 - 1.7.21 + 1.7.25 A reference implementation of OpenID Connect (http://openid.net/connect/), OAuth 2.0, and UMA built on top of Java, Spring, and Spring Security. The project contains a fully functioning server, client, and utility library. https://github.com/mitreid-connect @@ -80,48 +80,43 @@ org.apache.maven.plugins maven-jar-plugin - 2.4 + 3.0.2 org.apache.maven.plugins maven-war-plugin - 2.6 - - - org.apache.maven.plugins - maven-archiver - 2.5 + 3.0.0 org.apache.maven.plugins maven-javadoc-plugin - 2.9 + 2.10.4 org.apache.maven.plugins maven-deploy-plugin - 2.7 + 2.8.2 org.apache.maven.plugins maven-source-plugin - 2.2 + 3.0.1 org.apache.maven.plugins maven-compiler-plugin - 2.5.1 + 3.6.1 org.eclipse.jetty jetty-maven-plugin - 9.3.3.v20150827 + 9.4.3.v20170317 true org.apache.maven.plugins maven-enforcer-plugin - 1.3 + 1.4.1 org.appfuse.plugins @@ -131,24 +126,43 @@ org.apache.maven.plugins maven-site-plugin - 3.3 + 3.6 org.apache.maven.plugins maven-project-info-reports-plugin 2.9 + - org.lesscss - lesscss-maven-plugin - 1.7.0.1.1 - - - - compile - - - + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + + org.appfuse.plugins + + + warpath-maven-plugin + + + [3.5.0,) + + + add-classes + + + + + + + + + ro.isdc.wro4j @@ -162,6 +176,13 @@ + + + ro.isdc.wro4j + wro4j-extensions + 1.8.0 + + @@ -336,7 +357,7 @@ org.springframework spring-framework-bom - 4.3.4.RELEASE + 4.3.7.RELEASE pom import @@ -345,26 +366,26 @@ com.fasterxml.jackson.core jackson-databind - 2.3.4 + 2.9.0.pr2 com.fasterxml.jackson.core jackson-annotations - 2.3.4 + 2.9.0.pr2 org.springframework.security spring-security-bom - 4.2.0.RELEASE + 4.2.2.RELEASE pom import org.springframework.security.oauth spring-security-oauth2 - 2.0.12.RELEASE + 2.1.0.RELEASE @@ -390,7 +411,7 @@ mysql mysql-connector-java - 5.1.34 + 6.0.6 org.hsqldb @@ -400,7 +421,7 @@ org.postgresql postgresql - 9.4-1201-jdbc4 + 42.0.0.jre7 com.oracle @@ -420,7 +441,7 @@ com.zaxxer HikariCP - 2.4.1 + 2.6.1 @@ -450,7 +471,7 @@ log4j log4j - 1.2.15 + 1.2.17 javax.mail @@ -476,13 +497,13 @@ junit junit - 4.7 + 4.12 test org.easymock easymock - 2.0 + 3.4 test @@ -543,7 +564,7 @@ com.google.guava guava - 20.0 + 21.0 com.google.code.gson @@ -553,7 +574,7 @@ org.apache.httpcomponents httpclient - 4.5.2 + 4.5.3 commons-logging @@ -564,7 +585,7 @@ com.nimbusds nimbus-jose-jwt - 4.33 + 4.34.2 org.bouncycastle diff --git a/uma-server-webapp/pom.xml b/uma-server-webapp/pom.xml index 81f4705057..0c48f65e8e 100644 --- a/uma-server-webapp/pom.xml +++ b/uma-server-webapp/pom.xml @@ -60,6 +60,7 @@ openid-connect-server-webapp + false @@ -73,41 +74,6 @@ - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - - org.appfuse.plugins - - - warpath-maven-plugin - - - [3.5.0,) - - - add-classes - - - - - - - - - - - -
    From 2ea5f8fd28ed89efd1584bd9366e30d28b3c0c59 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 24 Mar 2017 12:41:14 -0400 Subject: [PATCH 177/269] =?UTF-8?q?sync=E2=80=99d=20databases=20with=20HSQ?= =?UTF-8?q?L=20schema,=20closes=20#1212?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../db/mysql/mysql_database_tables.sql | 56 +++++++++++++------ .../db/oracle/oracle_database_tables.sql | 24 ++++++++ .../db/psql/psql_database_tables.sql | 54 +++++++++++++----- 3 files changed, 102 insertions(+), 32 deletions(-) diff --git a/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql index 04839343d5..ad876b6f40 100644 --- a/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql @@ -10,7 +10,8 @@ CREATE TABLE IF NOT EXISTS access_token ( refresh_token_id BIGINT, client_id BIGINT, auth_holder_id BIGINT, - approved_site_id BIGINT + approved_site_id BIGINT, + UNIQUE(token_value) ); CREATE TABLE IF NOT EXISTS access_token_permissions ( @@ -130,6 +131,7 @@ CREATE TABLE IF NOT EXISTS client_details ( dynamically_registered BOOLEAN DEFAULT false NOT NULL, allow_introspection BOOLEAN DEFAULT false NOT NULL, id_token_validity_seconds BIGINT DEFAULT 600 NOT NULL, + device_code_validity_seconds BIGINT, client_id VARCHAR(256), client_secret VARCHAR(2048), @@ -169,6 +171,8 @@ CREATE TABLE IF NOT EXISTS client_details ( clear_access_tokens_on_refresh BOOLEAN DEFAULT true NOT NULL, software_statement VARCHAR(4096), + software_id VARCHAR(2048), + software_version VARCHAR(2048), code_challenge_method VARCHAR(256), @@ -235,8 +239,6 @@ CREATE TABLE IF NOT EXISTS system_scope ( icon VARCHAR(256), restricted BOOLEAN DEFAULT false NOT NULL, default_scope BOOLEAN DEFAULT false NOT NULL, - structured BOOLEAN DEFAULT false NOT NULL, - structured_param_description VARCHAR(256), UNIQUE (scope) ); @@ -360,18 +362,38 @@ CREATE TABLE IF NOT EXISTS saved_registered_client ( registered_client VARCHAR(8192) ); +CREATE TABLE IF NOT EXISTS device_code ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + device_code VARCHAR(1024), + user_code VARCHAR(1024), + expiration TIMESTAMP NULL, + client_id VARCHAR(256), + approved BOOLEAN, + auth_holder_id BIGINT +); + +CREATE TABLE IF NOT EXISTS device_code_scope ( + owner_id BIGINT NOT NULL, + scope VARCHAR(256) NOT NULL +); + +CREATE TABLE IF NOT EXISTS device_code_request_parameter ( + owner_id BIGINT, + param VARCHAR(2048), + val VARCHAR(2048) +); -CREATE INDEX at_tv_idx ON access_token(token_value(767)); -CREATE INDEX ts_oi_idx ON token_scope(owner_id); -CREATE INDEX at_exp_idx ON access_token(expiration); -CREATE INDEX rf_ahi_idx ON refresh_token(auth_holder_id); -CREATE INDEX cd_ci_idx ON client_details(client_id); -CREATE INDEX at_ahi_idx ON access_token(auth_holder_id); -CREATE INDEX aha_oi_idx ON authentication_holder_authority(owner_id); -CREATE INDEX ahe_oi_idx ON authentication_holder_extension(owner_id); -CREATE INDEX ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); -CREATE INDEX ahri_oi_idx ON authentication_holder_resource_id(owner_id); -CREATE INDEX ahrt_oi_idx ON authentication_holder_response_type(owner_id); -CREATE INDEX ahs_oi_idx ON authentication_holder_scope(owner_id); -CREATE INDEX ac_ahi_idx ON authorization_code(auth_holder_id); -CREATE INDEX suaa_oi_idx ON saved_user_auth_authority(owner_id); +CREATE INDEX IF NOT EXISTS at_tv_idx ON access_token(token_value(767)); +CREATE INDEX IF NOT EXISTS ts_oi_idx ON token_scope(owner_id); +CREATE INDEX IF NOT EXISTS at_exp_idx ON access_token(expiration); +CREATE INDEX IF NOT EXISTS rf_ahi_idx ON refresh_token(auth_holder_id); +CREATE INDEX IF NOT EXISTS cd_ci_idx ON client_details(client_id); +CREATE INDEX IF NOT EXISTS at_ahi_idx ON access_token(auth_holder_id); +CREATE INDEX IF NOT EXISTS aha_oi_idx ON authentication_holder_authority(owner_id); +CREATE INDEX IF NOT EXISTS ahe_oi_idx ON authentication_holder_extension(owner_id); +CREATE INDEX IF NOT EXISTS ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); +CREATE INDEX IF NOT EXISTS ahri_oi_idx ON authentication_holder_resource_id(owner_id); +CREATE INDEX IF NOT EXISTS ahrt_oi_idx ON authentication_holder_response_type(owner_id); +CREATE INDEX IF NOT EXISTS ahs_oi_idx ON authentication_holder_scope(owner_id); +CREATE INDEX IF NOT EXISTS ac_ahi_idx ON authorization_code(auth_holder_id); +CREATE INDEX IF NOT EXISTS suaa_oi_idx ON saved_user_auth_authority(owner_id); diff --git a/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql index 440a3a08ab..7c48fe0e54 100644 --- a/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql @@ -146,6 +146,7 @@ CREATE TABLE client_details ( client_secret VARCHAR2(2048), access_token_validity_seconds NUMBER(19), refresh_token_validity_seconds NUMBER(19), + device_code_validity_seconds NUMBER(19), application_type VARCHAR2(256), client_name VARCHAR2(256), @@ -179,6 +180,8 @@ CREATE TABLE client_details ( initiate_login_uri VARCHAR2(2048), clear_access_tokens_on_refresh NUMBER(1) DEFAULT 1 NOT NULL, + software_statement VARCHAR(4096), + software_id VARCHAR(2048), software_statement VARCHAR2(4000), code_challenge_method VARCHAR2(256), @@ -395,6 +398,27 @@ CREATE TABLE saved_registered_client ( ); CREATE SEQUENCE saved_registered_client_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; +CREATE TABLE IF NOT EXISTS device_code ( + id NUMBER(19) NOT NULL PRIMARY KEY, + device_code VARCHAR2(1024), + user_code VARCHAR2(1024), + expiration TIMESTAMP, + client_id VARCHAR2(256), + approved BOOLEAN, + auth_holder_id NUMBER(19) +); + +CREATE TABLE IF NOT EXISTS device_code_scope ( + owner_id NUMBER(19) NOT NULL, + scope VARCHAR2(256) NOT NULL +); + +CREATE TABLE IF NOT EXISTS device_code_request_parameter ( + owner_id NUMBER(19), + param VARCHAR2(2048), + val VARCHAR2(2048) +); + CREATE INDEX at_tv_idx ON access_token(token_value); CREATE INDEX ts_oi_idx ON token_scope(owner_id); CREATE INDEX at_exp_idx ON access_token(expiration); diff --git a/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql index 47985f0ad3..d21e95be0b 100644 --- a/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql @@ -10,7 +10,8 @@ CREATE TABLE IF NOT EXISTS access_token ( refresh_token_id BIGINT, client_id BIGINT, auth_holder_id BIGINT, - approved_site_id BIGINT + approved_site_id BIGINT, + UNIQUE(token_value) ); CREATE TABLE IF NOT EXISTS access_token_permissions ( @@ -130,6 +131,7 @@ CREATE TABLE IF NOT EXISTS client_details ( dynamically_registered BOOLEAN DEFAULT false NOT NULL, allow_introspection BOOLEAN DEFAULT false NOT NULL, id_token_validity_seconds BIGINT DEFAULT 600 NOT NULL, + device_code_validity_seconds BIGINT, client_id VARCHAR(256), client_secret VARCHAR(2048), @@ -169,6 +171,8 @@ CREATE TABLE IF NOT EXISTS client_details ( clear_access_tokens_on_refresh BOOLEAN DEFAULT true NOT NULL, software_statement VARCHAR(4096), + software_id VARCHAR(2048), + software_version VARCHAR(2048), code_challenge_method VARCHAR(256), @@ -360,18 +364,38 @@ CREATE TABLE IF NOT EXISTS saved_registered_client ( registered_client VARCHAR(8192) ); +CREATE TABLE IF NOT EXISTS device_code ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + device_code VARCHAR(1024), + user_code VARCHAR(1024), + expiration TIMESTAMP NULL, + client_id VARCHAR(256), + approved BOOLEAN, + auth_holder_id BIGINT +); + +CREATE TABLE IF NOT EXISTS device_code_scope ( + owner_id BIGINT NOT NULL, + scope VARCHAR(256) NOT NULL +); + +CREATE TABLE IF NOT EXISTS device_code_request_parameter ( + owner_id BIGINT, + param VARCHAR(2048), + val VARCHAR(2048) +); -CREATE INDEX at_tv_idx ON access_token(token_value); -CREATE INDEX ts_oi_idx ON token_scope(owner_id); -CREATE INDEX at_exp_idx ON access_token(expiration); -CREATE INDEX rf_ahi_idx ON refresh_token(auth_holder_id); -CREATE INDEX cd_ci_idx ON client_details(client_id); -CREATE INDEX at_ahi_idx ON access_token(auth_holder_id); -CREATE INDEX aha_oi_idx ON authentication_holder_authority(owner_id); -CREATE INDEX ahe_oi_idx ON authentication_holder_extension(owner_id); -CREATE INDEX ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); -CREATE INDEX ahri_oi_idx ON authentication_holder_resource_id(owner_id); -CREATE INDEX ahrt_oi_idx ON authentication_holder_response_type(owner_id); -CREATE INDEX ahs_oi_idx ON authentication_holder_scope(owner_id); -CREATE INDEX ac_ahi_idx ON authorization_code(auth_holder_id); -CREATE INDEX suaa_oi_idx ON saved_user_auth_authority(owner_id); +CREATE INDEX IF NOT EXISTS at_tv_idx ON access_token(token_value); +CREATE INDEX IF NOT EXISTS ts_oi_idx ON token_scope(owner_id); +CREATE INDEX IF NOT EXISTS at_exp_idx ON access_token(expiration); +CREATE INDEX IF NOT EXISTS rf_ahi_idx ON refresh_token(auth_holder_id); +CREATE INDEX IF NOT EXISTS cd_ci_idx ON client_details(client_id); +CREATE INDEX IF NOT EXISTS at_ahi_idx ON access_token(auth_holder_id); +CREATE INDEX IF NOT EXISTS aha_oi_idx ON authentication_holder_authority(owner_id); +CREATE INDEX IF NOT EXISTS ahe_oi_idx ON authentication_holder_extension(owner_id); +CREATE INDEX IF NOT EXISTS ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); +CREATE INDEX IF NOT EXISTS ahri_oi_idx ON authentication_holder_resource_id(owner_id); +CREATE INDEX IF NOT EXISTS ahrt_oi_idx ON authentication_holder_response_type(owner_id); +CREATE INDEX IF NOT EXISTS ahs_oi_idx ON authentication_holder_scope(owner_id); +CREATE INDEX IF NOT EXISTS ac_ahi_idx ON authorization_code(auth_holder_id); +CREATE INDEX IF NOT EXISTS suaa_oi_idx ON saved_user_auth_authority(owner_id); From 1d7fba5d6e23850da48f40311c48c19c01e3bf60 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 24 Mar 2017 12:44:49 -0400 Subject: [PATCH 178/269] added cascade to address object, closes #1209 --- .../java/org/mitre/openid/connect/model/DefaultUserInfo.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java index 5fe70b5c07..32efea98bb 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java @@ -21,6 +21,7 @@ import java.io.ObjectOutputStream; import javax.persistence.Basic; +import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Convert; import javax.persistence.Entity; @@ -369,7 +370,7 @@ public void setPhoneNumberVerified(Boolean phoneNumberVerified) { * @see org.mitre.openid.connect.model.UserInfo#getAddress() */ @Override - @OneToOne(targetEntity = DefaultAddress.class) + @OneToOne(targetEntity = DefaultAddress.class, cascade = CascadeType.ALL) @JoinColumn(name="address_id") public Address getAddress() { return address; From 835a32662768837e67415220d4dd14445c669642 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 27 Mar 2017 14:39:40 -0500 Subject: [PATCH 179/269] allow polling of device codes, fixed UI for device code input --- .../oauth2/service/DeviceCodeService.java | 10 +++++- .../webapp/WEB-INF/views/approveDevice.jsp | 9 ++--- .../webapp/WEB-INF/views/requestUserCode.jsp | 2 +- .../impl/DefaultDeviceCodeService.java | 34 ++++++++++++++----- .../oauth2/token/DeviceTokenGranter.java | 8 +++-- 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java index a0067b909a..35968b9e07 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java @@ -48,8 +48,16 @@ public interface DeviceCodeService { * @param client * @return */ - public DeviceCode consumeDeviceCode(String deviceCode, ClientDetails client); + public DeviceCode findDeviceCode(String deviceCode, ClientDetails client); + + /** + * + * @param deviceCode + * @param client + */ + public void clearDeviceCode(String deviceCode, ClientDetails client); + /** * @param deviceCode * @param userCode diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approveDevice.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approveDevice.jsp index bcc6882ca2..c49e1e8741 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approveDevice.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approveDevice.jsp @@ -37,7 +37,7 @@ + action="${pageContext.request.contextPath.endsWith('/') ? pageContext.request.contextPath : pageContext.request.contextPath.concat('/') }device/approve" method="post">
    @@ -163,8 +163,9 @@
    +
      - +
    • @@ -199,9 +200,9 @@ - +
    • - +
    diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp index c629d0b9f0..69a6ed3016 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/requestUserCode.jsp @@ -39,7 +39,7 @@ - +
    diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java index 8020de7a63..1d8f6282e8 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java @@ -90,22 +90,26 @@ public DeviceCode approveDeviceCode(DeviceCode dc, OAuth2Authentication auth) { * @see org.mitre.oauth2.service.DeviceCodeService#consumeDeviceCode(java.lang.String, org.springframework.security.oauth2.provider.ClientDetails) */ @Override - public DeviceCode consumeDeviceCode(String deviceCode, ClientDetails client) { + public DeviceCode findDeviceCode(String deviceCode, ClientDetails client) { DeviceCode found = repository.getByDeviceCode(deviceCode); - // make sure it's not used twice - repository.remove(found); - - if (found.getClientId().equals(client.getClientId())) { - // make sure the client matches, if so, we're good - return found; + if (found != null) { + if (found.getClientId().equals(client.getClientId())) { + // make sure the client matches, if so, we're good + return found; + } else { + // if the clients don't match, pretend the code wasn't found + return null; + } } else { - // if the clients don't match, pretend the code wasn't found + // didn't find the code, return null return null; } } + + /* (non-Javadoc) * @see org.mitre.oauth2.service.DeviceCodeService#clearExpiredDeviceCodes() */ @@ -126,4 +130,18 @@ protected void doOperation(DeviceCode item) { }.execute(); } + /* (non-Javadoc) + * @see org.mitre.oauth2.service.DeviceCodeService#clearDeviceCode(java.lang.String, org.springframework.security.oauth2.provider.ClientDetails) + */ + @Override + public void clearDeviceCode(String deviceCode, ClientDetails client) { + DeviceCode found = findDeviceCode(deviceCode, client); + + if (found != null) { + // make sure it's not used twice + repository.remove(found); + } + + } + } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java b/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java index c9a6c24310..34fde5410e 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/token/DeviceTokenGranter.java @@ -70,13 +70,15 @@ protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, Tok String deviceCode = tokenRequest.getRequestParameters().get("device_code"); // look up the device code and consume it - DeviceCode dc = deviceCodeService.consumeDeviceCode(deviceCode, client); + DeviceCode dc = deviceCodeService.findDeviceCode(deviceCode, client); if (dc != null) { // make sure the code hasn't expired yet if (dc.getExpiration() != null && dc.getExpiration().before(new Date())) { - // TODO: return an error + + deviceCodeService.clearDeviceCode(deviceCode, client); + throw new DeviceCodeExpiredException("Device code has expired " + deviceCode); } else if (!dc.isApproved()) { @@ -90,6 +92,8 @@ protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, Tok OAuth2Authentication auth = new OAuth2Authentication(getRequestFactory().createOAuth2Request(client, tokenRequest), dc.getAuthenticationHolder().getUserAuth()); + deviceCodeService.clearDeviceCode(deviceCode, client); + return auth; } } else { From bf49cd193d31f91c4ca2439d889856f187cc98c6 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 6 Apr 2017 14:07:39 -0400 Subject: [PATCH 180/269] removed incompatible constraints on mysql files --- .../db/mysql/mysql_database_tables.sql | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql index ad876b6f40..2f316be6e3 100644 --- a/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql @@ -10,8 +10,7 @@ CREATE TABLE IF NOT EXISTS access_token ( refresh_token_id BIGINT, client_id BIGINT, auth_holder_id BIGINT, - approved_site_id BIGINT, - UNIQUE(token_value) + approved_site_id BIGINT ); CREATE TABLE IF NOT EXISTS access_token_permissions ( @@ -383,17 +382,17 @@ CREATE TABLE IF NOT EXISTS device_code_request_parameter ( val VARCHAR(2048) ); -CREATE INDEX IF NOT EXISTS at_tv_idx ON access_token(token_value(767)); -CREATE INDEX IF NOT EXISTS ts_oi_idx ON token_scope(owner_id); -CREATE INDEX IF NOT EXISTS at_exp_idx ON access_token(expiration); -CREATE INDEX IF NOT EXISTS rf_ahi_idx ON refresh_token(auth_holder_id); -CREATE INDEX IF NOT EXISTS cd_ci_idx ON client_details(client_id); -CREATE INDEX IF NOT EXISTS at_ahi_idx ON access_token(auth_holder_id); -CREATE INDEX IF NOT EXISTS aha_oi_idx ON authentication_holder_authority(owner_id); -CREATE INDEX IF NOT EXISTS ahe_oi_idx ON authentication_holder_extension(owner_id); -CREATE INDEX IF NOT EXISTS ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); -CREATE INDEX IF NOT EXISTS ahri_oi_idx ON authentication_holder_resource_id(owner_id); -CREATE INDEX IF NOT EXISTS ahrt_oi_idx ON authentication_holder_response_type(owner_id); -CREATE INDEX IF NOT EXISTS ahs_oi_idx ON authentication_holder_scope(owner_id); -CREATE INDEX IF NOT EXISTS ac_ahi_idx ON authorization_code(auth_holder_id); -CREATE INDEX IF NOT EXISTS suaa_oi_idx ON saved_user_auth_authority(owner_id); +CREATE INDEX at_tv_idx ON access_token(token_value(767)); +CREATE INDEX ts_oi_idx ON token_scope(owner_id); +CREATE INDEX at_exp_idx ON access_token(expiration); +CREATE INDEX rf_ahi_idx ON refresh_token(auth_holder_id); +CREATE INDEX cd_ci_idx ON client_details(client_id); +CREATE INDEX at_ahi_idx ON access_token(auth_holder_id); +CREATE INDEX aha_oi_idx ON authentication_holder_authority(owner_id); +CREATE INDEX ahe_oi_idx ON authentication_holder_extension(owner_id); +CREATE INDEX ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); +CREATE INDEX ahri_oi_idx ON authentication_holder_resource_id(owner_id); +CREATE INDEX ahrt_oi_idx ON authentication_holder_response_type(owner_id); +CREATE INDEX ahs_oi_idx ON authentication_holder_scope(owner_id); +CREATE INDEX ac_ahi_idx ON authorization_code(auth_holder_id); +CREATE INDEX suaa_oi_idx ON saved_user_auth_authority(owner_id); From f43ff5368374250479bcb11e7b3dd480c2dc1ca5 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 6 Apr 2017 14:12:27 -0400 Subject: [PATCH 181/269] Extracted database indexes to separate files --- .../resources/db/hsql/hsql_database_index.sql | 18 ++++++++++++++++++ .../resources/db/hsql/hsql_database_tables.sql | 15 --------------- .../db/mysql/mysql_database_index.sql | 18 ++++++++++++++++++ .../db/mysql/mysql_database_tables.sql | 15 --------------- .../db/oracle/oracle_database_index.sql | 17 +++++++++++++++++ .../db/oracle/oracle_database_tables.sql | 14 -------------- .../resources/db/psql/psql_database_index.sql | 18 ++++++++++++++++++ .../resources/db/psql/psql_database_tables.sql | 15 --------------- 8 files changed, 71 insertions(+), 59 deletions(-) create mode 100644 openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_index.sql create mode 100644 openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_index.sql create mode 100644 openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_index.sql create mode 100644 openid-connect-server-webapp/src/main/resources/db/psql/psql_database_index.sql diff --git a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_index.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_index.sql new file mode 100644 index 0000000000..4f61d534f1 --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_index.sql @@ -0,0 +1,18 @@ +-- +-- Indexes for HSQLDB +-- + +CREATE INDEX IF NOT EXISTS at_tv_idx ON access_token(token_value); +CREATE INDEX IF NOT EXISTS ts_oi_idx ON token_scope(owner_id); +CREATE INDEX IF NOT EXISTS at_exp_idx ON access_token(expiration); +CREATE INDEX IF NOT EXISTS rf_ahi_idx ON refresh_token(auth_holder_id); +CREATE INDEX IF NOT EXISTS cd_ci_idx ON client_details(client_id); +CREATE INDEX IF NOT EXISTS at_ahi_idx ON access_token(auth_holder_id); +CREATE INDEX IF NOT EXISTS aha_oi_idx ON authentication_holder_authority(owner_id); +CREATE INDEX IF NOT EXISTS ahe_oi_idx ON authentication_holder_extension(owner_id); +CREATE INDEX IF NOT EXISTS ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); +CREATE INDEX IF NOT EXISTS ahri_oi_idx ON authentication_holder_resource_id(owner_id); +CREATE INDEX IF NOT EXISTS ahrt_oi_idx ON authentication_holder_response_type(owner_id); +CREATE INDEX IF NOT EXISTS ahs_oi_idx ON authentication_holder_scope(owner_id); +CREATE INDEX IF NOT EXISTS ac_ahi_idx ON authorization_code(auth_holder_id); +CREATE INDEX IF NOT EXISTS suaa_oi_idx ON saved_user_auth_authority(owner_id); diff --git a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql index 4274e53566..2a01756298 100644 --- a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql @@ -382,18 +382,3 @@ CREATE TABLE IF NOT EXISTS device_code_request_parameter ( param VARCHAR(2048), val VARCHAR(2048) ); - -CREATE INDEX IF NOT EXISTS at_tv_idx ON access_token(token_value); -CREATE INDEX IF NOT EXISTS ts_oi_idx ON token_scope(owner_id); -CREATE INDEX IF NOT EXISTS at_exp_idx ON access_token(expiration); -CREATE INDEX IF NOT EXISTS rf_ahi_idx ON refresh_token(auth_holder_id); -CREATE INDEX IF NOT EXISTS cd_ci_idx ON client_details(client_id); -CREATE INDEX IF NOT EXISTS at_ahi_idx ON access_token(auth_holder_id); -CREATE INDEX IF NOT EXISTS aha_oi_idx ON authentication_holder_authority(owner_id); -CREATE INDEX IF NOT EXISTS ahe_oi_idx ON authentication_holder_extension(owner_id); -CREATE INDEX IF NOT EXISTS ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); -CREATE INDEX IF NOT EXISTS ahri_oi_idx ON authentication_holder_resource_id(owner_id); -CREATE INDEX IF NOT EXISTS ahrt_oi_idx ON authentication_holder_response_type(owner_id); -CREATE INDEX IF NOT EXISTS ahs_oi_idx ON authentication_holder_scope(owner_id); -CREATE INDEX IF NOT EXISTS ac_ahi_idx ON authorization_code(auth_holder_id); -CREATE INDEX IF NOT EXISTS suaa_oi_idx ON saved_user_auth_authority(owner_id); diff --git a/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_index.sql b/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_index.sql new file mode 100644 index 0000000000..bbd5236e3d --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_index.sql @@ -0,0 +1,18 @@ +-- +-- Indexes for MySQL +-- + +CREATE INDEX at_tv_idx ON access_token(token_value(767)); +CREATE INDEX ts_oi_idx ON token_scope(owner_id); +CREATE INDEX at_exp_idx ON access_token(expiration); +CREATE INDEX rf_ahi_idx ON refresh_token(auth_holder_id); +CREATE INDEX cd_ci_idx ON client_details(client_id); +CREATE INDEX at_ahi_idx ON access_token(auth_holder_id); +CREATE INDEX aha_oi_idx ON authentication_holder_authority(owner_id); +CREATE INDEX ahe_oi_idx ON authentication_holder_extension(owner_id); +CREATE INDEX ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); +CREATE INDEX ahri_oi_idx ON authentication_holder_resource_id(owner_id); +CREATE INDEX ahrt_oi_idx ON authentication_holder_response_type(owner_id); +CREATE INDEX ahs_oi_idx ON authentication_holder_scope(owner_id); +CREATE INDEX ac_ahi_idx ON authorization_code(auth_holder_id); +CREATE INDEX suaa_oi_idx ON saved_user_auth_authority(owner_id); diff --git a/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql index 2f316be6e3..7e00cc8762 100644 --- a/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/mysql/mysql_database_tables.sql @@ -381,18 +381,3 @@ CREATE TABLE IF NOT EXISTS device_code_request_parameter ( param VARCHAR(2048), val VARCHAR(2048) ); - -CREATE INDEX at_tv_idx ON access_token(token_value(767)); -CREATE INDEX ts_oi_idx ON token_scope(owner_id); -CREATE INDEX at_exp_idx ON access_token(expiration); -CREATE INDEX rf_ahi_idx ON refresh_token(auth_holder_id); -CREATE INDEX cd_ci_idx ON client_details(client_id); -CREATE INDEX at_ahi_idx ON access_token(auth_holder_id); -CREATE INDEX aha_oi_idx ON authentication_holder_authority(owner_id); -CREATE INDEX ahe_oi_idx ON authentication_holder_extension(owner_id); -CREATE INDEX ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); -CREATE INDEX ahri_oi_idx ON authentication_holder_resource_id(owner_id); -CREATE INDEX ahrt_oi_idx ON authentication_holder_response_type(owner_id); -CREATE INDEX ahs_oi_idx ON authentication_holder_scope(owner_id); -CREATE INDEX ac_ahi_idx ON authorization_code(auth_holder_id); -CREATE INDEX suaa_oi_idx ON saved_user_auth_authority(owner_id); diff --git a/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_index.sql b/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_index.sql new file mode 100644 index 0000000000..135d8819cb --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_index.sql @@ -0,0 +1,17 @@ +-- +-- Indexes for Oracle +-- + +CREATE INDEX at_tv_idx ON access_token(token_value); +CREATE INDEX ts_oi_idx ON token_scope(owner_id); +CREATE INDEX at_exp_idx ON access_token(expiration); +CREATE INDEX rf_ahi_idx ON refresh_token(auth_holder_id); +CREATE INDEX at_ahi_idx ON access_token(auth_holder_id); +CREATE INDEX aha_oi_idx ON authentication_holder_authority(owner_id); +CREATE INDEX ahe_oi_idx ON authentication_holder_extension(owner_id); +CREATE INDEX ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); +CREATE INDEX ahri_oi_idx ON authentication_holder_resource_id(owner_id); +CREATE INDEX ahrt_oi_idx ON authentication_holder_response_type(owner_id); +CREATE INDEX ahs_oi_idx ON authentication_holder_scope(owner_id); +CREATE INDEX ac_ahi_idx ON authorization_code(auth_holder_id); +CREATE INDEX suaa_oi_idx ON saved_user_auth_authority(owner_id); diff --git a/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql index 7c48fe0e54..5f88b689bf 100644 --- a/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql @@ -418,17 +418,3 @@ CREATE TABLE IF NOT EXISTS device_code_request_parameter ( param VARCHAR2(2048), val VARCHAR2(2048) ); - -CREATE INDEX at_tv_idx ON access_token(token_value); -CREATE INDEX ts_oi_idx ON token_scope(owner_id); -CREATE INDEX at_exp_idx ON access_token(expiration); -CREATE INDEX rf_ahi_idx ON refresh_token(auth_holder_id); -CREATE INDEX at_ahi_idx ON access_token(auth_holder_id); -CREATE INDEX aha_oi_idx ON authentication_holder_authority(owner_id); -CREATE INDEX ahe_oi_idx ON authentication_holder_extension(owner_id); -CREATE INDEX ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); -CREATE INDEX ahri_oi_idx ON authentication_holder_resource_id(owner_id); -CREATE INDEX ahrt_oi_idx ON authentication_holder_response_type(owner_id); -CREATE INDEX ahs_oi_idx ON authentication_holder_scope(owner_id); -CREATE INDEX ac_ahi_idx ON authorization_code(auth_holder_id); -CREATE INDEX suaa_oi_idx ON saved_user_auth_authority(owner_id); diff --git a/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_index.sql b/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_index.sql new file mode 100644 index 0000000000..2eabf4f57a --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_index.sql @@ -0,0 +1,18 @@ +-- +-- Indexes for PostgreSQL +-- + +CREATE INDEX IF NOT EXISTS at_tv_idx ON access_token(token_value); +CREATE INDEX IF NOT EXISTS ts_oi_idx ON token_scope(owner_id); +CREATE INDEX IF NOT EXISTS at_exp_idx ON access_token(expiration); +CREATE INDEX IF NOT EXISTS rf_ahi_idx ON refresh_token(auth_holder_id); +CREATE INDEX IF NOT EXISTS cd_ci_idx ON client_details(client_id); +CREATE INDEX IF NOT EXISTS at_ahi_idx ON access_token(auth_holder_id); +CREATE INDEX IF NOT EXISTS aha_oi_idx ON authentication_holder_authority(owner_id); +CREATE INDEX IF NOT EXISTS ahe_oi_idx ON authentication_holder_extension(owner_id); +CREATE INDEX IF NOT EXISTS ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); +CREATE INDEX IF NOT EXISTS ahri_oi_idx ON authentication_holder_resource_id(owner_id); +CREATE INDEX IF NOT EXISTS ahrt_oi_idx ON authentication_holder_response_type(owner_id); +CREATE INDEX IF NOT EXISTS ahs_oi_idx ON authentication_holder_scope(owner_id); +CREATE INDEX IF NOT EXISTS ac_ahi_idx ON authorization_code(auth_holder_id); +CREATE INDEX IF NOT EXISTS suaa_oi_idx ON saved_user_auth_authority(owner_id); diff --git a/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql index d21e95be0b..d43016a3cd 100644 --- a/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql @@ -384,18 +384,3 @@ CREATE TABLE IF NOT EXISTS device_code_request_parameter ( param VARCHAR(2048), val VARCHAR(2048) ); - -CREATE INDEX IF NOT EXISTS at_tv_idx ON access_token(token_value); -CREATE INDEX IF NOT EXISTS ts_oi_idx ON token_scope(owner_id); -CREATE INDEX IF NOT EXISTS at_exp_idx ON access_token(expiration); -CREATE INDEX IF NOT EXISTS rf_ahi_idx ON refresh_token(auth_holder_id); -CREATE INDEX IF NOT EXISTS cd_ci_idx ON client_details(client_id); -CREATE INDEX IF NOT EXISTS at_ahi_idx ON access_token(auth_holder_id); -CREATE INDEX IF NOT EXISTS aha_oi_idx ON authentication_holder_authority(owner_id); -CREATE INDEX IF NOT EXISTS ahe_oi_idx ON authentication_holder_extension(owner_id); -CREATE INDEX IF NOT EXISTS ahrp_oi_idx ON authentication_holder_request_parameter(owner_id); -CREATE INDEX IF NOT EXISTS ahri_oi_idx ON authentication_holder_resource_id(owner_id); -CREATE INDEX IF NOT EXISTS ahrt_oi_idx ON authentication_holder_response_type(owner_id); -CREATE INDEX IF NOT EXISTS ahs_oi_idx ON authentication_holder_scope(owner_id); -CREATE INDEX IF NOT EXISTS ac_ahi_idx ON authorization_code(auth_holder_id); -CREATE INDEX IF NOT EXISTS suaa_oi_idx ON saved_user_auth_authority(owner_id); From 2aecedfb3d273552e15d41b702ddc87e1b168025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= Date: Tue, 4 Apr 2017 15:27:22 +0200 Subject: [PATCH 182/269] Replace cobertura with jacoco to generate code-coverage for codecov.io Change-Id: I031144c375d73aaa39d0a6111d37223ad6e4d655 --- pom.xml | 60 +++++++++++++++++++++------------------------------------ 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/pom.xml b/pom.xml index d4901e67b9..157847c978 100644 --- a/pom.xml +++ b/pom.xml @@ -2,13 +2,13 @@ - - - - - - - - - - - @@ -512,14 +509,6 @@ 1.9.5 test - - org.codehaus.mojo - cobertura-maven-plugin - 2.7 - test - maven-plugin - - org.mitre @@ -650,11 +639,6 @@ javax.servlet.jsp jsp-api - - org.codehaus.mojo - cobertura-maven-plugin - maven-plugin - From 9d1a50d17ed186bf0d2b3aad6493a173cc8981fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= Date: Tue, 4 Apr 2017 16:09:34 +0200 Subject: [PATCH 183/269] Add codecov badge Change-Id: I04f56036af6bd2ac663843c2c10e8c0cebfb05d3 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f0160151a9..ade8050989 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MITREid Connect --- -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.mitre/openid-connect-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.mitre/openid-connect-parent) [![Travis CI](https://travis-ci.org/mitreid-connect/OpenID-Connect-Java-Spring-Server.svg?branch=master)](https://travis-ci.org/mitreid-connect/OpenID-Connect-Java-Spring-Server) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.mitre/openid-connect-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.mitre/openid-connect-parent) [![Travis CI](https://travis-ci.org/mitreid-connect/OpenID-Connect-Java-Spring-Server.svg?branch=master)](https://travis-ci.org/mitreid-connect/OpenID-Connect-Java-Spring-Server) [![Codecov](https://codecov.io/github/mitreid-connect/OpenID-Connect-Java-Spring-Server/coverage.svg?branch=master)](https://codecov.io/github/mitreid-connect/OpenID-Connect-Java-Spring-Server) This project contains a certified OpenID Connect reference implementation in Java on the Spring platform, including a functioning [server library](openid-connect-server), [deployable server package](openid-connect-server-webapp), [client (RP) library](openid-connect-client), and general [utility libraries](openid-connect-common). The server can be used as an OpenID Connect Identity Provider as well as a general-purpose OAuth 2.0 Authorization Server. From 6216659cd6584e6589c66f687d4f378f7dfdd411 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 7 Apr 2017 14:58:11 -0400 Subject: [PATCH 184/269] manage reporting plugin versions, added jacoco coverage to reporting --- pom.xml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 157847c978..45f533ac69 100644 --- a/pom.xml +++ b/pom.xml @@ -92,6 +92,16 @@ maven-javadoc-plugin 2.10.4 + + org.jacoco + jacoco-maven-plugin + 0.7.9 + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.10 + org.apache.maven.plugins maven-deploy-plugin @@ -253,7 +263,6 @@ org.jacoco jacoco-maven-plugin - 0.7.9 @@ -277,7 +286,6 @@ org.apache.maven.plugins maven-project-info-reports-plugin - 2.4 false false @@ -304,7 +312,6 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9 true true @@ -319,7 +326,6 @@ org.apache.maven.plugins maven-checkstyle-plugin - 2.10 checkstyle.xml @@ -334,6 +340,10 @@ + + org.jacoco + jacoco-maven-plugin + From 903168a94917487c6da53103051f5a64115caa0c Mon Sep 17 00:00:00 2001 From: Mark Janssen Date: Wed, 22 Mar 2017 22:15:02 +0100 Subject: [PATCH 185/269] Decrease log level of trailing slash warning Having an issuer without trailing slash configured is just fine, so there is no reason to log a warning for this every time the discovery endpoint is called. --- .../main/java/org/mitre/discovery/web/DiscoveryEndpoint.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java b/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java index 0c2df3b060..ada2a15c5e 100644 --- a/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java @@ -287,7 +287,7 @@ OPTIONAL. JSON array containing a list of the JWS signing algorithms (alg values String baseUrl = config.getIssuer(); if (!baseUrl.endsWith("/")) { - logger.warn("Configured issuer doesn't end in /, adding for discovery: " + baseUrl); + logger.debug("Configured issuer doesn't end in /, adding for discovery: {}", baseUrl); baseUrl = baseUrl.concat("/"); } From 52829d4adbd90b2dc624be1fd0a0bd58d20bf3da Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 7 Apr 2017 17:02:50 -0400 Subject: [PATCH 186/269] mapped user info and server config interceptors to only interactive portions of the site, closes #1206 --- .../webapp/WEB-INF/application-context.xml | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml index 49cb8d3e95..4f70b20a04 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml @@ -44,10 +44,41 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From cc0622edd0f754b853963735337f93b7b7c14092 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 12 Apr 2017 14:59:18 -0400 Subject: [PATCH 187/269] internalized random string generation for device codes --- .../mitre/oauth2/service/DeviceCodeService.java | 2 +- .../service/impl/DefaultDeviceCodeService.java | 12 +++++++++++- .../java/org/mitre/oauth2/web/DeviceEndpoint.java | 14 +++----------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java index 35968b9e07..f75a4b2910 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java @@ -66,7 +66,7 @@ public interface DeviceCodeService { * @param parameters * @return */ - public DeviceCode createNewDeviceCode(String deviceCode, String userCode, Set requestedScopes, ClientDetailsEntity client, Map parameters); + public DeviceCode createNewDeviceCode(Set requestedScopes, ClientDetailsEntity client, Map parameters); public void clearExpiredDeviceCodes(); diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java index 1d8f6282e8..fcdc12527a 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java @@ -21,6 +21,7 @@ import java.util.Date; import java.util.Map; import java.util.Set; +import java.util.UUID; import org.mitre.data.AbstractPageOperationTemplate; import org.mitre.oauth2.model.AuthenticationHolderEntity; @@ -29,6 +30,7 @@ import org.mitre.oauth2.repository.impl.DeviceCodeRepository; import org.mitre.oauth2.service.DeviceCodeService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.oauth2.common.util.RandomValueStringGenerator; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.stereotype.Service; @@ -44,11 +46,19 @@ public class DefaultDeviceCodeService implements DeviceCodeService { @Autowired private DeviceCodeRepository repository; + private RandomValueStringGenerator randomGenerator = new RandomValueStringGenerator(); + /* (non-Javadoc) * @see org.mitre.oauth2.service.DeviceCodeService#save(org.mitre.oauth2.model.DeviceCode) */ @Override - public DeviceCode createNewDeviceCode(String deviceCode, String userCode, Set requestedScopes, ClientDetailsEntity client, Map parameters) { + public DeviceCode createNewDeviceCode(Set requestedScopes, ClientDetailsEntity client, Map parameters) { + + // create a device code, should be big and random + String deviceCode = UUID.randomUUID().toString(); + + // create a user code, should be random but small and typable + String userCode = randomGenerator.generate(); DeviceCode dc = new DeviceCode(deviceCode, userCode, requestedScopes, client.getClientId(), parameters); diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java index f141e8cce5..f746f85f8e 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java @@ -91,8 +91,6 @@ public class DeviceEndpoint { @Autowired private OAuth2RequestFactory oAuth2RequestFactory; - private RandomValueStringGenerator randomGenerator = new RandomValueStringGenerator(); - @RequestMapping(value = "/" + URL, method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public String requestDeviceCode(@RequestParam("client_id") String clientId, @RequestParam(name="scope", required=false) String scope, Map parameters, ModelMap model) { @@ -134,17 +132,11 @@ public String requestDeviceCode(@RequestParam("client_id") String clientId, @Req // if we got here the request is legit - // create a device code, should be big and random - String deviceCode = UUID.randomUUID().toString(); - - // create a user code, should be random but small and typable - String userCode = randomGenerator.generate(); - - deviceCodeService.createNewDeviceCode(deviceCode, userCode, requestedScopes, client, parameters); + DeviceCode dc = deviceCodeService.createNewDeviceCode(requestedScopes, client, parameters); Map response = new HashMap<>(); - response.put("device_code", deviceCode); - response.put("user_code", userCode); + response.put("device_code", dc.getDeviceCode()); + response.put("user_code", dc.getUserCode()); response.put("verification_uri", config.getIssuer() + USER_URL); if (client.getDeviceCodeValiditySeconds() != null) { response.put("expires_in", client.getDeviceCodeValiditySeconds()); From d317cf502430113717d4c83b3d063cab157f8cbb Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 12 Apr 2017 15:59:17 -0400 Subject: [PATCH 188/269] added exception handling to device code creation step --- .../DeviceCodeCreationException.java | 51 +++++++++++++++++++ .../oauth2/service/DeviceCodeService.java | 3 +- .../org/mitre/oauth2/web/DeviceEndpoint.java | 36 ++++++++----- 3 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 openid-connect-common/src/main/java/org/mitre/oauth2/exception/DeviceCodeCreationException.java diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/exception/DeviceCodeCreationException.java b/openid-connect-common/src/main/java/org/mitre/oauth2/exception/DeviceCodeCreationException.java new file mode 100644 index 0000000000..a4dd7d1caf --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/exception/DeviceCodeCreationException.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * 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.mitre.oauth2.exception; + +/** + * @author jricher + * + */ +public class DeviceCodeCreationException extends Exception { + + private static final long serialVersionUID = 8078568710169208466L; + + private String error; + + public DeviceCodeCreationException(String error, String message) { + super(message); + this.error = error; + } + + /** + * @return the error + */ + public String getError() { + return error; + } + + /** + * @param error the error to set + */ + public void setError(String error) { + this.error = error; + } + + + +} diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java index f75a4b2910..aa7fa7b863 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/DeviceCodeService.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.Set; +import org.mitre.oauth2.exception.DeviceCodeCreationException; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.DeviceCode; import org.springframework.security.oauth2.provider.ClientDetails; @@ -66,7 +67,7 @@ public interface DeviceCodeService { * @param parameters * @return */ - public DeviceCode createNewDeviceCode(Set requestedScopes, ClientDetailsEntity client, Map parameters); + public DeviceCode createNewDeviceCode(Set requestedScopes, ClientDetailsEntity client, Map parameters) throws DeviceCodeCreationException; public void clearExpiredDeviceCodes(); diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java index f746f85f8e..580732dff3 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java @@ -27,6 +27,7 @@ import javax.servlet.http.HttpSession; +import org.mitre.oauth2.exception.DeviceCodeCreationException; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.DeviceCode; import org.mitre.oauth2.model.SystemScope; @@ -132,21 +133,30 @@ public String requestDeviceCode(@RequestParam("client_id") String clientId, @Req // if we got here the request is legit - DeviceCode dc = deviceCodeService.createNewDeviceCode(requestedScopes, client, parameters); - - Map response = new HashMap<>(); - response.put("device_code", dc.getDeviceCode()); - response.put("user_code", dc.getUserCode()); - response.put("verification_uri", config.getIssuer() + USER_URL); - if (client.getDeviceCodeValiditySeconds() != null) { - response.put("expires_in", client.getDeviceCodeValiditySeconds()); + try { + DeviceCode dc = deviceCodeService.createNewDeviceCode(requestedScopes, client, parameters); + + Map response = new HashMap<>(); + response.put("device_code", dc.getDeviceCode()); + response.put("user_code", dc.getUserCode()); + response.put("verification_uri", config.getIssuer() + USER_URL); + if (client.getDeviceCodeValiditySeconds() != null) { + response.put("expires_in", client.getDeviceCodeValiditySeconds()); + } + + model.put(JsonEntityView.ENTITY, response); + + + return JsonEntityView.VIEWNAME; + } catch (DeviceCodeCreationException dcce) { + + model.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST); + model.put(JsonErrorView.ERROR, dcce.getError()); + model.put(JsonErrorView.ERROR_MESSAGE, dcce.getMessage()); + + return JsonErrorView.VIEWNAME; } - model.put(JsonEntityView.ENTITY, response); - - - return JsonEntityView.VIEWNAME; - } @PreAuthorize("hasRole('ROLE_USER')") From 45ea899de8aa6b15b60409285a1bb744ef2b1c55 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 12 Apr 2017 16:00:23 -0400 Subject: [PATCH 189/269] made user codes case insensitive --- .../oauth2/service/impl/DefaultDeviceCodeService.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java index fcdc12527a..2dc8c23619 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java @@ -57,8 +57,8 @@ public DeviceCode createNewDeviceCode(Set requestedScopes, ClientDetails // create a device code, should be big and random String deviceCode = UUID.randomUUID().toString(); - // create a user code, should be random but small and typable - String userCode = randomGenerator.generate(); + // create a user code, should be random but small and typable, and always uppercase (lookup is case insensitive) + String userCode = randomGenerator.generate().toUpperCase(); DeviceCode dc = new DeviceCode(deviceCode, userCode, requestedScopes, client.getClientId(), parameters); @@ -76,7 +76,8 @@ public DeviceCode createNewDeviceCode(Set requestedScopes, ClientDetails */ @Override public DeviceCode lookUpByUserCode(String userCode) { - return repository.getByUserCode(userCode); + // always up-case the code for lookup + return repository.getByUserCode(userCode.toUpperCase()); } /* (non-Javadoc) From 702a775881b29d75e78d845b50c476e83c159c00 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 13 Apr 2017 11:50:57 -0400 Subject: [PATCH 190/269] handle creation time stamp in clients, closes #1210 --- .../openid/connect/service/impl/MITREidDataService_1_3.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java index 582d17f32c..2f922f9ed4 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_3.java @@ -529,6 +529,7 @@ private void writeClients(JsonWriter writer) { writer.name(SOFTWARE_ID).value(client.getSoftwareId()); writer.name(SOFTWARE_VERSION).value(client.getSoftwareVersion()); writer.name(SOFTWARE_STATEMENT).value(client.getSoftwareStatement() != null ? client.getSoftwareStatement().serialize() : null); + writer.name(CREATION_DATE).value(toUTCString(client.getCreatedAt())); writer.endObject(); logger.debug("Wrote client {}", client.getId()); } catch (IOException ex) { @@ -1169,6 +1170,9 @@ private void readClients(JsonReader reader) throws IOException { } catch (ParseException e) { logger.error("Couldn't parse software statement", e); } + } else if (name.equals(CREATION_DATE)) { + Date date = utcToDate(reader.nextString()); + client.setCreatedAt(date); } else { logger.debug("Found unexpected entry"); reader.skipValue(); From 11f3cccab95f84009f6cdbd203c54243b9a94954 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 14 Apr 2017 14:42:49 -0400 Subject: [PATCH 191/269] fix JWKS parsing in software statements, closes #1220 --- .../src/main/java/org/mitre/openid/connect/web/ClientAPI.java | 2 +- .../openid/connect/web/DynamicClientRegistrationEndpoint.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ClientAPI.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ClientAPI.java index 7d924f14d8..9f9f4e0ae0 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ClientAPI.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ClientAPI.java @@ -608,7 +608,7 @@ private ClientDetailsEntity validateSoftwareStatement(ClientDetailsEntity newCli newClient.setJwksUri(claimSet.getStringClaim(claim)); break; case JWKS: - newClient.setJwks(JWKSet.parse(claimSet.getStringClaim(claim))); + newClient.setJwks(JWKSet.parse(claimSet.getJSONObjectClaim(claim).toJSONString())); break; case POLICY_URI: newClient.setPolicyUri(claimSet.getStringClaim(claim)); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DynamicClientRegistrationEndpoint.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DynamicClientRegistrationEndpoint.java index f99e5cc669..c95dca5457 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DynamicClientRegistrationEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DynamicClientRegistrationEndpoint.java @@ -688,7 +688,7 @@ private ClientDetailsEntity validateSoftwareStatement(ClientDetailsEntity newCli newClient.setJwksUri(claimSet.getStringClaim(claim)); break; case JWKS: - newClient.setJwks(JWKSet.parse(claimSet.getStringClaim(claim))); + newClient.setJwks(JWKSet.parse(claimSet.getJSONObjectClaim(claim).toJSONString())); break; case POLICY_URI: newClient.setPolicyUri(claimSet.getStringClaim(claim)); From 0d564d97144a898b3efc1b9ec99191985084e811 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 14 Apr 2017 15:27:16 -0400 Subject: [PATCH 192/269] made token service transactional, closes #1222 --- .../service/impl/DefaultOAuth2ProviderTokenService.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java index 6b9d2d9aff..fe256243b9 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java @@ -62,6 +62,7 @@ import org.springframework.security.oauth2.provider.TokenRequest; import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import com.google.common.collect.Sets; import com.nimbusds.jose.util.Base64URL; @@ -177,6 +178,7 @@ private OAuth2RefreshTokenEntity clearExpiredRefreshToken(OAuth2RefreshTokenEnti } @Override + @Transactional(value="defaultTransactionManager") public OAuth2AccessTokenEntity createAccessToken(OAuth2Authentication authentication) throws AuthenticationException, InvalidClientException { if (authentication != null && authentication.getOAuth2Request() != null) { // look up our client @@ -310,6 +312,7 @@ private OAuth2RefreshTokenEntity createRefreshToken(ClientDetailsEntity client, } @Override + @Transactional(value="defaultTransactionManager") public OAuth2AccessTokenEntity refreshAccessToken(String refreshTokenValue, TokenRequest authRequest) throws AuthenticationException { OAuth2RefreshTokenEntity refreshToken = clearExpiredRefreshToken(tokenRepository.getRefreshTokenByValue(refreshTokenValue)); @@ -455,6 +458,7 @@ public OAuth2RefreshTokenEntity getRefreshToken(String refreshTokenValue) throws * Revoke a refresh token and all access tokens issued to it. */ @Override + @Transactional(value="defaultTransactionManager") public void revokeRefreshToken(OAuth2RefreshTokenEntity refreshToken) { tokenRepository.clearAccessTokensForRefreshToken(refreshToken); tokenRepository.removeRefreshToken(refreshToken); @@ -464,6 +468,7 @@ public void revokeRefreshToken(OAuth2RefreshTokenEntity refreshToken) { * Revoke an access token. */ @Override + @Transactional(value="defaultTransactionManager") public void revokeAccessToken(OAuth2AccessTokenEntity accessToken) { tokenRepository.removeAccessToken(accessToken); } @@ -533,6 +538,7 @@ public void doOperation(AuthenticationHolderEntity item) { * @see org.mitre.oauth2.service.OAuth2TokenEntityService#saveAccessToken(org.mitre.oauth2.model.OAuth2AccessTokenEntity) */ @Override + @Transactional(value="defaultTransactionManager") public OAuth2AccessTokenEntity saveAccessToken(OAuth2AccessTokenEntity accessToken) { OAuth2AccessTokenEntity newToken = tokenRepository.saveAccessToken(accessToken); @@ -548,6 +554,7 @@ public OAuth2AccessTokenEntity saveAccessToken(OAuth2AccessTokenEntity accessTok * @see org.mitre.oauth2.service.OAuth2TokenEntityService#saveRefreshToken(org.mitre.oauth2.model.OAuth2RefreshTokenEntity) */ @Override + @Transactional(value="defaultTransactionManager") public OAuth2RefreshTokenEntity saveRefreshToken(OAuth2RefreshTokenEntity refreshToken) { return tokenRepository.saveRefreshToken(refreshToken); } From 0aedfc8e227c311a0dd7ad72cf050fcdd52c92be Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 14 Apr 2017 17:21:50 -0400 Subject: [PATCH 193/269] minor cleanup --- .../src/main/webapp/WEB-INF/views/login.jsp | 1 - 1 file changed, 1 deletion(-) diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/login.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/login.jsp index fb71cab2a2..5be8f9b2a5 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/login.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/login.jsp @@ -3,7 +3,6 @@ <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ taglib prefix="o" tagdir="/WEB-INF/tags"%> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> - \ No newline at end of file + From 1feb0958bd0757eb4452ed3d2aeb9b002e9260cd Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 7 Feb 2018 09:09:07 -0500 Subject: [PATCH 228/269] prepare for release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d1bc15d86..159e487317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ Unreleased: + +*1.3.2: - Added changelog - Set default redirect URI resolver strict matching to true - Fixed XSS vulnerability on redirect URI display on approval page From f72e6b3e08a5f9b85b7fcc6673055cb144164288 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 7 Feb 2018 09:14:10 -0500 Subject: [PATCH 229/269] [maven-release-plugin] prepare release mitreid-connect-1.3.2 --- openid-connect-client/pom.xml | 2 +- openid-connect-common/pom.xml | 2 +- openid-connect-server-webapp/pom.xml | 2 +- openid-connect-server/pom.xml | 2 +- pom.xml | 2 +- uma-server-webapp/pom.xml | 2 +- uma-server/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml index 31467161c1..ffebb6025f 100644 --- a/openid-connect-client/pom.xml +++ b/openid-connect-client/pom.xml @@ -22,7 +22,7 @@ openid-connect-parent org.mitre - 1.3.2-SNAPSHOT + 1.3.2 .. openid-connect-client diff --git a/openid-connect-common/pom.xml b/openid-connect-common/pom.xml index b01cd971be..d1863fd7da 100644 --- a/openid-connect-common/pom.xml +++ b/openid-connect-common/pom.xml @@ -22,7 +22,7 @@ openid-connect-parent org.mitre - 1.3.2-SNAPSHOT + 1.3.2 .. openid-connect-common diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index b96cb4b39f..1e072cf5a2 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -21,7 +21,7 @@ org.mitre openid-connect-parent - 1.3.2-SNAPSHOT + 1.3.2 openid-connect-server-webapp war diff --git a/openid-connect-server/pom.xml b/openid-connect-server/pom.xml index f3650b984a..a789a82e5a 100644 --- a/openid-connect-server/pom.xml +++ b/openid-connect-server/pom.xml @@ -23,7 +23,7 @@ org.mitre openid-connect-parent - 1.3.2-SNAPSHOT + 1.3.2 .. diff --git a/pom.xml b/pom.xml index 4ef0bc8c03..3df892977b 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.mitre openid-connect-parent - 1.3.2-SNAPSHOT + 1.3.2 MITREid Connect pom diff --git a/uma-server-webapp/pom.xml b/uma-server-webapp/pom.xml index 5c2d8ebf15..96d7797984 100644 --- a/uma-server-webapp/pom.xml +++ b/uma-server-webapp/pom.xml @@ -19,7 +19,7 @@ org.mitre openid-connect-parent - 1.3.2-SNAPSHOT + 1.3.2 .. uma-server-webapp diff --git a/uma-server/pom.xml b/uma-server/pom.xml index f956423b2d..6a7b061a2b 100644 --- a/uma-server/pom.xml +++ b/uma-server/pom.xml @@ -19,7 +19,7 @@ org.mitre openid-connect-parent - 1.3.2-SNAPSHOT + 1.3.2 .. uma-server From b804f22bc879a391ecbecf3dcc99325a6a803ee8 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 7 Feb 2018 09:14:16 -0500 Subject: [PATCH 230/269] [maven-release-plugin] prepare for next development iteration --- openid-connect-client/pom.xml | 2 +- openid-connect-common/pom.xml | 2 +- openid-connect-server-webapp/pom.xml | 2 +- openid-connect-server/pom.xml | 2 +- pom.xml | 2 +- uma-server-webapp/pom.xml | 2 +- uma-server/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml index ffebb6025f..d3bd77eb6b 100644 --- a/openid-connect-client/pom.xml +++ b/openid-connect-client/pom.xml @@ -22,7 +22,7 @@ openid-connect-parent org.mitre - 1.3.2 + 1.3.3-SNAPSHOT .. openid-connect-client diff --git a/openid-connect-common/pom.xml b/openid-connect-common/pom.xml index d1863fd7da..6b7772cd40 100644 --- a/openid-connect-common/pom.xml +++ b/openid-connect-common/pom.xml @@ -22,7 +22,7 @@ openid-connect-parent org.mitre - 1.3.2 + 1.3.3-SNAPSHOT .. openid-connect-common diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index 1e072cf5a2..6bc66e4f99 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -21,7 +21,7 @@ org.mitre openid-connect-parent - 1.3.2 + 1.3.3-SNAPSHOT openid-connect-server-webapp war diff --git a/openid-connect-server/pom.xml b/openid-connect-server/pom.xml index a789a82e5a..1462be27ea 100644 --- a/openid-connect-server/pom.xml +++ b/openid-connect-server/pom.xml @@ -23,7 +23,7 @@ org.mitre openid-connect-parent - 1.3.2 + 1.3.3-SNAPSHOT .. diff --git a/pom.xml b/pom.xml index 3df892977b..72d9126efc 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.mitre openid-connect-parent - 1.3.2 + 1.3.3-SNAPSHOT MITREid Connect pom diff --git a/uma-server-webapp/pom.xml b/uma-server-webapp/pom.xml index 96d7797984..8ce3302da1 100644 --- a/uma-server-webapp/pom.xml +++ b/uma-server-webapp/pom.xml @@ -19,7 +19,7 @@ org.mitre openid-connect-parent - 1.3.2 + 1.3.3-SNAPSHOT .. uma-server-webapp diff --git a/uma-server/pom.xml b/uma-server/pom.xml index 6a7b061a2b..2191615285 100644 --- a/uma-server/pom.xml +++ b/uma-server/pom.xml @@ -19,7 +19,7 @@ org.mitre openid-connect-parent - 1.3.2 + 1.3.3-SNAPSHOT .. uma-server From d119559d4d3d2fae141aed63d112328c8f8b2c6c Mon Sep 17 00:00:00 2001 From: Mark Janssen Date: Thu, 8 Feb 2018 21:03:31 +0100 Subject: [PATCH 231/269] Upgrade to Spring Security 4.2.4 https://spring.io/blog/2018/01/30/cve-2018-1199-spring-security-5-0-1-4-2-4-4-1-5-released --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 72d9126efc..06466d2ab0 100644 --- a/pom.xml +++ b/pom.xml @@ -386,7 +386,7 @@ org.springframework.security spring-security-bom - 4.2.2.RELEASE + 4.2.4.RELEASE pom import From e2d94f422a09a463854ff0919d560641cd06ef23 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 12 Feb 2018 10:39:04 -0500 Subject: [PATCH 232/269] new year 2018 --- LICENSE.txt | 2 +- README.md | 2 +- README_zh_CN.md | 2 +- checkstyle.xml | 2 +- openid-connect-client/pom.xml | 2 +- .../oauth2/introspectingfilter/IntrospectingTokenService.java | 2 +- .../oauth2/introspectingfilter/OAuth2AccessTokenImpl.java | 2 +- .../service/IntrospectionAuthorityGranter.java | 2 +- .../service/IntrospectionConfigurationService.java | 2 +- .../impl/JWTParsingIntrospectionConfigurationService.java | 2 +- .../impl/ScopeBasedIntrospectionAuthoritiesGranter.java | 2 +- .../service/impl/SimpleIntrospectionAuthorityGranter.java | 2 +- .../service/impl/StaticIntrospectionConfigurationService.java | 2 +- .../openid/connect/client/AuthorizationEndpointException.java | 2 +- .../openid/connect/client/NamedAdminAuthoritiesMapper.java | 2 +- .../mitre/openid/connect/client/OIDCAuthenticationFilter.java | 2 +- .../openid/connect/client/OIDCAuthenticationProvider.java | 2 +- .../mitre/openid/connect/client/OIDCAuthoritiesMapper.java | 2 +- .../connect/client/StaticPrefixTargetLinkURIChecker.java | 2 +- .../openid/connect/client/SubjectIssuerGrantedAuthority.java | 2 +- .../org/mitre/openid/connect/client/TargetLinkURIChecker.java | 2 +- .../java/org/mitre/openid/connect/client/UserInfoFetcher.java | 2 +- .../connect/client/keypublisher/ClientKeyPublisher.java | 2 +- .../client/keypublisher/ClientKeyPublisherMapping.java | 2 +- .../openid/connect/client/keypublisher/JwkViewResolver.java | 2 +- .../openid/connect/client/model/IssuerServiceResponse.java | 2 +- .../connect/client/service/AuthRequestOptionsService.java | 2 +- .../openid/connect/client/service/AuthRequestUrlBuilder.java | 2 +- .../connect/client/service/ClientConfigurationService.java | 2 +- .../mitre/openid/connect/client/service/IssuerService.java | 2 +- .../connect/client/service/RegisteredClientService.java | 2 +- .../connect/client/service/ServerConfigurationService.java | 2 +- .../impl/DynamicRegistrationClientConfigurationService.java | 2 +- .../service/impl/DynamicServerConfigurationService.java | 2 +- .../client/service/impl/EncryptedAuthRequestUrlBuilder.java | 2 +- .../client/service/impl/HybridClientConfigurationService.java | 2 +- .../connect/client/service/impl/HybridIssuerService.java | 2 +- .../client/service/impl/HybridServerConfigurationService.java | 2 +- .../client/service/impl/InMemoryRegisteredClientService.java | 2 +- .../client/service/impl/JsonFileRegisteredClientService.java | 2 +- .../client/service/impl/PlainAuthRequestUrlBuilder.java | 2 +- .../client/service/impl/SignedAuthRequestUrlBuilder.java | 2 +- .../client/service/impl/StaticAuthRequestOptionsService.java | 2 +- .../client/service/impl/StaticClientConfigurationService.java | 2 +- .../client/service/impl/StaticServerConfigurationService.java | 2 +- .../client/service/impl/StaticSingleIssuerService.java | 2 +- .../connect/client/service/impl/ThirdPartyIssuerService.java | 2 +- .../connect/client/service/impl/WebfingerIssuerService.java | 2 +- .../oauth2/introspectingfilter/TestOAuth2AccessTokenImpl.java | 2 +- .../impl/TestScopeBasedIntrospectionAuthoritiesGranter.java | 2 +- .../openid/connect/client/TestOIDCAuthenticationFilter.java | 2 +- .../service/impl/TestHybridClientConfigurationService.java | 2 +- .../service/impl/TestHybridServerConfigurationService.java | 2 +- .../client/service/impl/TestPlainAuthRequestUrlBuilder.java | 2 +- .../client/service/impl/TestSignedAuthRequestUrlBuilder.java | 2 +- .../service/impl/TestStaticClientConfigurationService.java | 2 +- .../service/impl/TestStaticServerConfigurationService.java | 2 +- .../client/service/impl/TestThirdPartyIssuerService.java | 2 +- openid-connect-client/src/test/resources/test-context.xml | 2 +- openid-connect-common/pom.xml | 2 +- .../java/org/mitre/data/AbstractPageOperationTemplate.java | 2 +- .../src/main/java/org/mitre/data/DefaultPageCriteria.java | 2 +- .../src/main/java/org/mitre/data/PageCriteria.java | 2 +- .../java/org/mitre/discovery/util/WebfingerURLNormalizer.java | 2 +- .../src/main/java/org/mitre/jose/keystore/JWKSetKeyStore.java | 2 +- .../main/java/org/mitre/jwt/assertion/AssertionValidator.java | 2 +- .../org/mitre/jwt/assertion/impl/NullAssertionValidator.java | 2 +- .../org/mitre/jwt/assertion/impl/SelfAssertionValidator.java | 2 +- .../assertion/impl/WhitelistedIssuerAssertionValidator.java | 2 +- .../encryption/service/JWTEncryptionAndDecryptionService.java | 2 +- .../impl/DefaultJWTEncryptionAndDecryptionService.java | 2 +- .../jwt/signer/service/JWTSigningAndValidationService.java | 2 +- .../mitre/jwt/signer/service/impl/ClientKeyCacheService.java | 2 +- .../service/impl/DefaultJWTSigningAndValidationService.java | 2 +- .../org/mitre/jwt/signer/service/impl/JWKSetCacheService.java | 2 +- .../service/impl/SymmetricKeyJWTValidatorCacheService.java | 2 +- .../mitre/oauth2/exception/DeviceCodeCreationException.java | 2 +- .../org/mitre/oauth2/model/AuthenticationHolderEntity.java | 2 +- .../java/org/mitre/oauth2/model/AuthorizationCodeEntity.java | 2 +- .../main/java/org/mitre/oauth2/model/ClientDetailsEntity.java | 2 +- .../src/main/java/org/mitre/oauth2/model/DeviceCode.java | 2 +- .../java/org/mitre/oauth2/model/OAuth2AccessTokenEntity.java | 2 +- .../java/org/mitre/oauth2/model/OAuth2RefreshTokenEntity.java | 2 +- .../src/main/java/org/mitre/oauth2/model/PKCEAlgorithm.java | 2 +- .../main/java/org/mitre/oauth2/model/RegisteredClient.java | 2 +- .../java/org/mitre/oauth2/model/RegisteredClientFields.java | 2 +- .../java/org/mitre/oauth2/model/SavedUserAuthentication.java | 2 +- .../src/main/java/org/mitre/oauth2/model/SystemScope.java | 2 +- .../oauth2/model/convert/JWEAlgorithmStringConverter.java | 2 +- .../model/convert/JWEEncryptionMethodStringConverter.java | 2 +- .../org/mitre/oauth2/model/convert/JWKSetStringConverter.java | 2 +- .../oauth2/model/convert/JWSAlgorithmStringConverter.java | 2 +- .../org/mitre/oauth2/model/convert/JWTStringConverter.java | 2 +- .../oauth2/model/convert/JsonElementStringConverter.java | 2 +- .../oauth2/model/convert/PKCEAlgorithmStringConverter.java | 2 +- .../oauth2/model/convert/SerializableStringConverter.java | 2 +- .../model/convert/SimpleGrantedAuthorityStringConverter.java | 2 +- .../oauth2/repository/AuthenticationHolderRepository.java | 2 +- .../mitre/oauth2/repository/AuthorizationCodeRepository.java | 2 +- .../org/mitre/oauth2/repository/OAuth2ClientRepository.java | 2 +- .../org/mitre/oauth2/repository/OAuth2TokenRepository.java | 2 +- .../org/mitre/oauth2/repository/SystemScopeRepository.java | 2 +- .../mitre/oauth2/repository/impl/DeviceCodeRepository.java | 2 +- .../org/mitre/oauth2/service/ClientDetailsEntityService.java | 2 +- .../main/java/org/mitre/oauth2/service/DeviceCodeService.java | 2 +- .../mitre/oauth2/service/IntrospectionResultAssembler.java | 2 +- .../org/mitre/oauth2/service/OAuth2TokenEntityService.java | 2 +- .../java/org/mitre/oauth2/service/SystemScopeService.java | 2 +- .../oauth2/service/impl/DefaultClientUserDetailsService.java | 2 +- .../service/impl/UriEncodedClientUserDetailsService.java | 2 +- .../openid/connect/ClientDetailsEntityJsonProcessor.java | 2 +- .../connect/config/ConfigurationBeanLocaleResolver.java | 2 +- .../openid/connect/config/ConfigurationPropertiesBean.java | 2 +- .../java/org/mitre/openid/connect/config/JWKSetEditor.java | 2 +- .../org/mitre/openid/connect/config/ServerConfiguration.java | 2 +- .../java/org/mitre/openid/connect/config/UIConfiguration.java | 2 +- .../src/main/java/org/mitre/openid/connect/model/Address.java | 2 +- .../java/org/mitre/openid/connect/model/ApprovedSite.java | 2 +- .../java/org/mitre/openid/connect/model/BlacklistedSite.java | 2 +- .../main/java/org/mitre/openid/connect/model/CachedImage.java | 2 +- .../main/java/org/mitre/openid/connect/model/ClientStat.java | 2 +- .../java/org/mitre/openid/connect/model/DefaultAddress.java | 2 +- .../java/org/mitre/openid/connect/model/DefaultUserInfo.java | 2 +- .../mitre/openid/connect/model/OIDCAuthenticationToken.java | 2 +- .../org/mitre/openid/connect/model/PairwiseIdentifier.java | 2 +- .../openid/connect/model/PendingOIDCAuthenticationToken.java | 2 +- .../main/java/org/mitre/openid/connect/model/UserInfo.java | 2 +- .../java/org/mitre/openid/connect/model/WhitelistedSite.java | 2 +- .../connect/model/convert/JsonObjectStringConverter.java | 2 +- .../mitre/openid/connect/repository/AddressRepository.java | 2 +- .../openid/connect/repository/ApprovedSiteRepository.java | 2 +- .../openid/connect/repository/BlacklistedSiteRepository.java | 2 +- .../connect/repository/PairwiseIdentifierRepository.java | 2 +- .../mitre/openid/connect/repository/UserInfoRepository.java | 2 +- .../openid/connect/repository/WhitelistedSiteRepository.java | 2 +- .../org/mitre/openid/connect/service/ApprovedSiteService.java | 2 +- .../mitre/openid/connect/service/BlacklistedSiteService.java | 2 +- .../openid/connect/service/ClientLogoLoadingService.java | 2 +- .../org/mitre/openid/connect/service/LoginHintExtracter.java | 2 +- .../org/mitre/openid/connect/service/MITREidDataService.java | 2 +- .../openid/connect/service/MITREidDataServiceExtension.java | 2 +- .../mitre/openid/connect/service/MITREidDataServiceMaps.java | 2 +- .../org/mitre/openid/connect/service/OIDCTokenService.java | 2 +- .../openid/connect/service/PairwiseIdentiferService.java | 2 +- .../openid/connect/service/ScopeClaimTranslationService.java | 2 +- .../java/org/mitre/openid/connect/service/StatsService.java | 2 +- .../org/mitre/openid/connect/service/UserInfoService.java | 2 +- .../mitre/openid/connect/service/WhitelistedSiteService.java | 2 +- .../main/java/org/mitre/openid/connect/view/JWKSetView.java | 2 +- .../org/mitre/openid/connect/web/UserInfoInterceptor.java | 2 +- .../src/main/java/org/mitre/uma/model/Claim.java | 2 +- .../main/java/org/mitre/uma/model/ClaimProcessingResult.java | 2 +- .../src/main/java/org/mitre/uma/model/Permission.java | 2 +- .../src/main/java/org/mitre/uma/model/PermissionTicket.java | 2 +- .../src/main/java/org/mitre/uma/model/Policy.java | 2 +- .../src/main/java/org/mitre/uma/model/ResourceSet.java | 2 +- .../main/java/org/mitre/uma/model/SavedRegisteredClient.java | 2 +- .../uma/model/convert/RegisteredClientStringConverter.java | 2 +- .../java/org/mitre/uma/repository/PermissionRepository.java | 2 +- .../java/org/mitre/uma/repository/ResourceSetRepository.java | 2 +- .../java/org/mitre/uma/service/ClaimsProcessingService.java | 2 +- .../main/java/org/mitre/uma/service/PermissionService.java | 2 +- .../main/java/org/mitre/uma/service/ResourceSetService.java | 2 +- .../org/mitre/uma/service/SavedRegisteredClientService.java | 2 +- .../src/main/java/org/mitre/uma/service/UmaTokenService.java | 2 +- .../src/main/java/org/mitre/util/JsonUtils.java | 2 +- .../src/main/java/org/mitre/util/jpa/JpaUtil.java | 2 +- .../org/mitre/data/AbstractPageOperationTemplateTest.java | 2 +- .../org/mitre/discovery/util/TestWebfingerURLNormalizer.java | 2 +- .../src/test/java/org/mitre/jose/TestJWKSetKeyStore.java | 2 +- .../impl/TestDefaultJWTEncryptionAndDecryptionService.java | 2 +- .../java/org/mitre/oauth2/model/ClientDetailsEntityTest.java | 2 +- .../java/org/mitre/oauth2/model/RegisteredClientTest.java | 2 +- .../openid/connect/ClientDetailsEntityJsonProcessorTest.java | 2 +- .../connect/config/ConfigurationPropertiesBeanTest.java | 2 +- .../mitre/openid/connect/config/ServerConfigurationTest.java | 2 +- openid-connect-server-webapp/pom.xml | 2 +- .../src/main/resources/db/oracle/entity-mappings_oracle.xml | 2 +- openid-connect-server-webapp/src/main/resources/log4j.xml | 2 +- .../src/main/webapp/WEB-INF/application-context.xml | 2 +- .../src/main/webapp/WEB-INF/assertion-config.xml | 2 +- .../src/main/webapp/WEB-INF/authz-config.xml | 2 +- .../src/main/webapp/WEB-INF/crypto-config.xml | 2 +- .../src/main/webapp/WEB-INF/data-context.xml | 2 +- .../src/main/webapp/WEB-INF/endpoint-config.xml | 2 +- .../src/main/webapp/WEB-INF/jpa-config.xml | 2 +- .../src/main/webapp/WEB-INF/local-config.xml | 2 +- .../src/main/webapp/WEB-INF/locale-config.xml | 2 +- .../src/main/webapp/WEB-INF/server-config.xml | 2 +- .../src/main/webapp/WEB-INF/spring-servlet.xml | 2 +- .../src/main/webapp/WEB-INF/task-config.xml | 2 +- .../src/main/webapp/WEB-INF/ui-config.xml | 2 +- .../src/main/webapp/WEB-INF/user-context.xml | 2 +- openid-connect-server-webapp/src/main/webapp/WEB-INF/web.xml | 2 +- .../src/main/webapp/WEB-INF/wro.properties | 2 +- openid-connect-server-webapp/src/main/webapp/WEB-INF/wro.xml | 2 +- .../src/main/webapp/resources/js/admin.js | 2 +- .../src/main/webapp/resources/js/blacklist.js | 2 +- .../src/main/webapp/resources/js/client.js | 2 +- .../src/main/webapp/resources/js/dynreg.js | 2 +- .../src/main/webapp/resources/js/grant.js | 2 +- .../src/main/webapp/resources/js/locale/en/messages.json | 2 +- .../src/main/webapp/resources/js/locale/fr/messages.json | 2 +- .../src/main/webapp/resources/js/profile.js | 2 +- .../src/main/webapp/resources/js/rsreg.js | 2 +- .../src/main/webapp/resources/js/scope.js | 2 +- .../src/main/webapp/resources/js/token.js | 2 +- .../src/main/webapp/resources/js/whitelist.js | 2 +- .../src/main/webapp/resources/template/admin.html | 2 +- .../src/main/webapp/resources/template/blacklist.html | 2 +- .../src/main/webapp/resources/template/client.html | 2 +- .../src/main/webapp/resources/template/dynreg.html | 2 +- .../src/main/webapp/resources/template/grant.html | 2 +- .../src/main/webapp/resources/template/rsreg.html | 2 +- .../src/main/webapp/resources/template/scope.html | 2 +- .../src/main/webapp/resources/template/token.html | 2 +- .../src/main/webapp/resources/template/whitelist.html | 2 +- openid-connect-server/pom.xml | 2 +- .../src/main/java/org/mitre/discovery/view/WebfingerView.java | 2 +- .../main/java/org/mitre/discovery/web/DiscoveryEndpoint.java | 2 +- .../mitre/oauth2/assertion/AssertionOAuth2RequestFactory.java | 2 +- .../mitre/oauth2/assertion/impl/DirectCopyRequestFactory.java | 2 +- .../mitre/oauth2/exception/AuthorizationPendingException.java | 2 +- .../mitre/oauth2/exception/DeviceCodeExpiredException.java | 2 +- .../mitre/oauth2/exception/DuplicateClientIdException.java | 2 +- .../repository/impl/JpaAuthenticationHolderRepository.java | 2 +- .../repository/impl/JpaAuthorizationCodeRepository.java | 2 +- .../mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java | 2 +- .../oauth2/repository/impl/JpaOAuth2ClientRepository.java | 2 +- .../oauth2/repository/impl/JpaOAuth2TokenRepository.java | 2 +- .../oauth2/repository/impl/JpaSystemScopeRepository.java | 2 +- .../oauth2/service/impl/BlacklistAwareRedirectResolver.java | 2 +- .../mitre/oauth2/service/impl/DefaultDeviceCodeService.java | 2 +- .../service/impl/DefaultIntrospectionResultAssembler.java | 2 +- .../service/impl/DefaultOAuth2AuthorizationCodeService.java | 2 +- .../service/impl/DefaultOAuth2ClientDetailsEntityService.java | 2 +- .../service/impl/DefaultOAuth2ProviderTokenService.java | 2 +- .../mitre/oauth2/service/impl/DefaultSystemScopeService.java | 2 +- .../main/java/org/mitre/oauth2/token/ChainedTokenGranter.java | 2 +- .../main/java/org/mitre/oauth2/token/DeviceTokenGranter.java | 2 +- .../java/org/mitre/oauth2/token/JWTAssertionTokenGranter.java | 2 +- .../oauth2/token/ScopeServiceAwareOAuth2RequestValidator.java | 2 +- .../src/main/java/org/mitre/oauth2/view/TokenApiView.java | 2 +- .../java/org/mitre/oauth2/web/AuthenticationUtilities.java | 2 +- .../src/main/java/org/mitre/oauth2/web/CorsFilter.java | 2 +- .../src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java | 2 +- .../main/java/org/mitre/oauth2/web/IntrospectionEndpoint.java | 2 +- .../java/org/mitre/oauth2/web/OAuth2ExceptionHandler.java | 2 +- .../org/mitre/oauth2/web/OAuthConfirmationController.java | 2 +- .../main/java/org/mitre/oauth2/web/RevocationEndpoint.java | 2 +- .../src/main/java/org/mitre/oauth2/web/ScopeAPI.java | 2 +- .../src/main/java/org/mitre/oauth2/web/TokenAPI.java | 2 +- .../assertion/JWTBearerAssertionAuthenticationToken.java | 2 +- .../connect/assertion/JWTBearerAuthenticationProvider.java | 2 +- .../JWTBearerClientAssertionTokenEndpointFilter.java | 2 +- .../org/mitre/openid/connect/config/JsonMessageSource.java | 2 +- .../mitre/openid/connect/exception/ValidationException.java | 4 ++-- .../openid/connect/filter/AuthorizationRequestFilter.java | 2 +- .../mitre/openid/connect/filter/MultiUrlRequestMatcher.java | 2 +- .../openid/connect/repository/impl/JpaAddressRepository.java | 2 +- .../connect/repository/impl/JpaApprovedSiteRepository.java | 2 +- .../connect/repository/impl/JpaBlacklistedSiteRepository.java | 2 +- .../repository/impl/JpaPairwiseIdentifierRepository.java | 2 +- .../openid/connect/repository/impl/JpaUserInfoRepository.java | 2 +- .../connect/repository/impl/JpaWhitelistedSiteRepository.java | 2 +- .../openid/connect/request/ConnectOAuth2RequestFactory.java | 2 +- .../openid/connect/request/ConnectRequestParameters.java | 2 +- .../connect/service/impl/DefaultApprovedSiteService.java | 2 +- .../connect/service/impl/DefaultBlacklistedSiteService.java | 2 +- .../openid/connect/service/impl/DefaultOIDCTokenService.java | 2 +- .../service/impl/DefaultScopeClaimTranslationService.java | 2 +- .../openid/connect/service/impl/DefaultStatsService.java | 2 +- .../openid/connect/service/impl/DefaultUserInfoService.java | 2 +- .../connect/service/impl/DefaultWhitelistedSiteService.java | 2 +- .../openid/connect/service/impl/DummyResourceSetService.java | 2 +- .../service/impl/InMemoryClientLogoLoadingService.java | 2 +- .../connect/service/impl/MITREidDataServiceSupport.java | 2 +- .../openid/connect/service/impl/MITREidDataService_1_0.java | 2 +- .../openid/connect/service/impl/MITREidDataService_1_1.java | 2 +- .../openid/connect/service/impl/MITREidDataService_1_2.java | 2 +- .../openid/connect/service/impl/MITREidDataService_1_3.java | 2 +- .../connect/service/impl/MatchLoginHintsAgainstUsers.java | 2 +- .../mitre/openid/connect/service/impl/PassAllLoginHints.java | 2 +- .../openid/connect/service/impl/RemoveLoginHintsWithHTTP.java | 2 +- .../connect/service/impl/UUIDPairwiseIdentiferService.java | 2 +- .../org/mitre/openid/connect/token/ConnectTokenEnhancer.java | 2 +- .../mitre/openid/connect/token/TofuUserApprovalHandler.java | 2 +- .../java/org/mitre/openid/connect/util/IdTokenHashUtils.java | 2 +- .../mitre/openid/connect/view/AbstractClientEntityView.java | 2 +- .../mitre/openid/connect/view/ClientEntityViewForAdmins.java | 2 +- .../mitre/openid/connect/view/ClientEntityViewForUsers.java | 2 +- .../openid/connect/view/ClientInformationResponseView.java | 2 +- .../main/java/org/mitre/openid/connect/view/HttpCodeView.java | 2 +- .../org/mitre/openid/connect/view/JsonApprovedSiteView.java | 2 +- .../java/org/mitre/openid/connect/view/JsonEntityView.java | 2 +- .../java/org/mitre/openid/connect/view/JsonErrorView.java | 2 +- .../java/org/mitre/openid/connect/view/UserInfoJWTView.java | 2 +- .../main/java/org/mitre/openid/connect/view/UserInfoView.java | 2 +- .../java/org/mitre/openid/connect/web/ApprovedSiteAPI.java | 2 +- .../mitre/openid/connect/web/AuthenticationTimeStamper.java | 2 +- .../main/java/org/mitre/openid/connect/web/BlacklistAPI.java | 2 +- .../src/main/java/org/mitre/openid/connect/web/ClientAPI.java | 2 +- .../src/main/java/org/mitre/openid/connect/web/DataAPI.java | 4 ++-- .../openid/connect/web/DynamicClientRegistrationEndpoint.java | 2 +- .../java/org/mitre/openid/connect/web/EndSessionEndpoint.java | 2 +- .../mitre/openid/connect/web/JWKSetPublishingEndpoint.java | 2 +- .../connect/web/ProtectedResourceRegistrationEndpoint.java | 2 +- .../java/org/mitre/openid/connect/web/RootController.java | 2 +- .../org/mitre/openid/connect/web/ServerConfigInterceptor.java | 2 +- .../src/main/java/org/mitre/openid/connect/web/StatsAPI.java | 2 +- .../java/org/mitre/openid/connect/web/UserInfoEndpoint.java | 2 +- .../main/java/org/mitre/openid/connect/web/WhitelistAPI.java | 2 +- .../service/impl/TestBlacklistAwareRedirectResolver.java | 2 +- .../service/impl/TestDefaultIntrospectionResultAssembler.java | 2 +- .../impl/TestDefaultOAuth2ClientDetailsEntityService.java | 2 +- .../service/impl/TestDefaultOAuth2ProviderTokenService.java | 2 +- .../oauth2/service/impl/TestDefaultSystemScopeService.java | 2 +- .../connect/service/impl/TestDefaultApprovedSiteService.java | 2 +- .../service/impl/TestDefaultBlacklistedSiteService.java | 2 +- .../openid/connect/service/impl/TestDefaultStatsService.java | 2 +- .../connect/service/impl/TestDefaultUserInfoService.java | 2 +- .../service/impl/TestDefaultWhitelistedSiteService.java | 2 +- .../connect/service/impl/TestMITREidDataService_1_0.java | 2 +- .../connect/service/impl/TestMITREidDataService_1_1.java | 2 +- .../connect/service/impl/TestMITREidDataService_1_2.java | 2 +- .../connect/service/impl/TestMITREidDataService_1_3.java | 2 +- .../service/impl/TestUUIDPairwiseIdentiferService.java | 2 +- .../mitre/openid/connect/token/TestConnectTokenEnhancer.java | 2 +- .../org/mitre/openid/connect/util/TestIdTokenHashUtils.java | 2 +- pom.xml | 2 +- uma-server-webapp/pom.xml | 2 +- uma-server-webapp/src/main/webapp/WEB-INF/endpoint-config.xml | 2 +- uma-server-webapp/src/main/webapp/WEB-INF/server-config.xml | 2 +- uma-server-webapp/src/main/webapp/WEB-INF/ui-config.xml | 2 +- uma-server-webapp/src/main/webapp/WEB-INF/user-context.xml | 2 +- uma-server-webapp/src/main/webapp/resources/js/policy.js | 2 +- .../src/main/webapp/resources/template/policy.html | 2 +- uma-server/pom.xml | 2 +- .../mitre/uma/repository/impl/JpaPermissionRepository.java | 2 +- .../mitre/uma/repository/impl/JpaResourceSetRepository.java | 2 +- .../org/mitre/uma/service/impl/DefaultPermissionService.java | 2 +- .../org/mitre/uma/service/impl/DefaultResourceSetService.java | 2 +- .../org/mitre/uma/service/impl/DefaultUmaTokenService.java | 2 +- .../mitre/uma/service/impl/JpaRegisteredClientService.java | 2 +- .../org/mitre/uma/service/impl/MatchAllClaimsOnAnyPolicy.java | 2 +- .../mitre/uma/service/impl/UmaDataServiceExtension_1_3.java | 2 +- .../org/mitre/uma/util/ExternalLoginAuthoritiesMapper.java | 2 +- .../org/mitre/uma/view/ResourceSetEntityAbbreviatedView.java | 2 +- .../main/java/org/mitre/uma/view/ResourceSetEntityView.java | 2 +- .../java/org/mitre/uma/web/AuthorizationRequestEndpoint.java | 2 +- .../main/java/org/mitre/uma/web/ClaimsCollectionEndpoint.java | 2 +- .../org/mitre/uma/web/PermissionRegistrationEndpoint.java | 2 +- uma-server/src/main/java/org/mitre/uma/web/PolicyAPI.java | 2 +- .../org/mitre/uma/web/ResourceSetRegistrationEndpoint.java | 2 +- .../src/main/java/org/mitre/uma/web/UmaDiscoveryEndpoint.java | 2 +- .../main/java/org/mitre/uma/web/UserClaimSearchHelper.java | 2 +- .../mitre/uma/service/impl/TestDefaultPermissionService.java | 2 +- .../mitre/uma/service/impl/TestDefaultResourceSetService.java | 2 +- 358 files changed, 360 insertions(+), 360 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index be3864a680..0e640e493b 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright 2017 The MIT Internet Trust Consortium +Copyright 2018 The MIT Internet Trust Consortium Portions copyright 2011-2013 The MITRE Corporation diff --git a/README.md b/README.md index 7b9646814f..febfbd6737 100644 --- a/README.md +++ b/README.md @@ -28,4 +28,4 @@ The authors and key contributors of the project include: * [Mark Janssen](https://github.com/praseodym) -Copyright ©2017, [MIT Internet Trust Consortium](http://www.trust.mit.edu/). Licensed under the Apache 2.0 license, for details see `LICENSE.txt`. +Copyright ©2018, [MIT Internet Trust Consortium](http://www.trust.mit.edu/). Licensed under the Apache 2.0 license, for details see `LICENSE.txt`. diff --git a/README_zh_CN.md b/README_zh_CN.md index fecf8aebea..4933b36836 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -35,4 +35,4 @@ -版权所有 ©2017 [MIT因特网信任联盟](http://www.mit-trust.org/). 采用Apache 2.0许可证, 详见 `LICENSE.txt`. +版权所有 ©2018 [MIT因特网信任联盟](http://www.mit-trust.org/). 采用Apache 2.0许可证, 详见 `LICENSE.txt`. diff --git a/checkstyle.xml b/checkstyle.xml index 4d12f82d13..06129daddb 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -1,6 +1,6 @@ + + + diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java index 9e2bac67f2..9c54c9f073 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java @@ -138,18 +138,21 @@ public String requestDeviceCode(@RequestParam("client_id") String clientId, @Req try { DeviceCode dc = deviceCodeService.createNewDeviceCode(requestedScopes, client, parameters); - URI verificationUriComplete = new URIBuilder(config.getIssuer() + USER_URL) - .addParameter("user_code", dc.getUserCode()) - .build(); - Map response = new HashMap<>(); response.put("device_code", dc.getDeviceCode()); response.put("user_code", dc.getUserCode()); response.put("verification_uri", config.getIssuer() + USER_URL); - response.put("verification_uri_complete", verificationUriComplete); if (client.getDeviceCodeValiditySeconds() != null) { response.put("expires_in", client.getDeviceCodeValiditySeconds()); } + + if (config.isAllowCompleteDeviceCodeUri()) { + URI verificationUriComplete = new URIBuilder(config.getIssuer() + USER_URL) + .addParameter("user_code", dc.getUserCode()) + .build(); + + response.put("verification_uri_complete", verificationUriComplete.toString()); + } model.put(JsonEntityView.ENTITY, response); @@ -175,8 +178,8 @@ public String requestDeviceCode(@RequestParam("client_id") String clientId, @Req @RequestMapping(value = "/" + USER_URL, method = RequestMethod.GET) public String requestUserCode(@RequestParam(value = "user_code", required = false) String userCode, ModelMap model, HttpSession session) { - if (userCode == null) { - + if (!config.isAllowCompleteDeviceCodeUri() || userCode == null) { + // if we don't allow the complete URI or we didn't get a user code on the way in, // print out a page that asks the user to enter their user code // user must be logged in return "requestUserCode"; From 802e40ebc9a03d96a1d8f46345c8260db9dc9853 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 3 May 2018 14:52:49 -0400 Subject: [PATCH 249/269] Updated changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 159e487317..5609bc7045 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ Unreleased: +- Authorization codes are now longer +- Client/RS can parse the "sub" and "user_id" claims in introspection response +- Database-direct queries for fetching tokens by user (optimization) +- Device flow supports verification_uri_complete (must be turned on) +- Long scopes display properly and are still checkable +- Language system remebers when it can't find a file and stops throwing so many errors +- Index added for refresh tokens +- Updated to Spring Security 4.2.4 *1.3.2: - Added changelog From 69afba59cc53ea2075609c157e9b142fd242f90e Mon Sep 17 00:00:00 2001 From: Angelo Kastroulis Date: Mon, 11 Jun 2018 15:48:09 -0400 Subject: [PATCH 250/269] Corrected create script --- .../src/main/resources/db/oracle/create_db-user | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 openid-connect-server-webapp/src/main/resources/db/oracle/create_db-user diff --git a/openid-connect-server-webapp/src/main/resources/db/oracle/create_db-user b/openid-connect-server-webapp/src/main/resources/db/oracle/create_db-user new file mode 100644 index 0000000000..fdbf9d44fb --- /dev/null +++ b/openid-connect-server-webapp/src/main/resources/db/oracle/create_db-user @@ -0,0 +1,15 @@ +drop user oauth cascade; +drop tablespace data_ts INCLUDING CONTENTS AND DATAFILES; +drop tablespace temp_ts INCLUDING CONTENTS AND DATAFILES; +CREATE TABLESPACE data_ts DATAFILE 'data_ts.dat' SIZE 40M ONLINE; +CREATE TEMPORARY TABLESPACE temp_ts TEMPFILE 'temp_ts.dbf' SIZE 5M AUTOEXTEND ON; +create user oauth identified by test DEFAULT TABLESPACE data_ts QUOTA 500K ON data_ts TEMPORARY TABLESPACE temp_ts; +GRANT CONNECT TO oauth; +GRANT UNLIMITED TABLESPACE TO oauth; +grant create session to oauth; +grant create table to oauth; +GRANT CREATE TABLESPACE TO oauth; +GRANT CREATE VIEW TO oauth; +GRANT CREATE ANY INDEX TO oauth; +GRANT CREATE SEQUENCE TO oauth; +GRANT CREATE SYNONYM TO oauth; From f56918982a22f668a4a519dc950f6e1d9e28c8d8 Mon Sep 17 00:00:00 2001 From: Angelo Kastroulis Date: Mon, 11 Jun 2018 15:50:05 -0400 Subject: [PATCH 251/269] Fixed broken scripts from schema change on system_scope --- .../src/main/resources/db/mysql/scopes.sql | 16 +++++----- .../db/oracle/oracle_database_tables.sql | 7 ++--- .../resources/db/oracle/scopes_oracle.sql | 31 +++++++++---------- .../db/psql/psql_database_tables.sql | 2 -- .../src/main/resources/db/psql/scopes.sql | 18 +++++------ 5 files changed, 34 insertions(+), 40 deletions(-) diff --git a/openid-connect-server-webapp/src/main/resources/db/mysql/scopes.sql b/openid-connect-server-webapp/src/main/resources/db/mysql/scopes.sql index 62d5dcd29a..3768977ec1 100644 --- a/openid-connect-server-webapp/src/main/resources/db/mysql/scopes.sql +++ b/openid-connect-server-webapp/src/main/resources/db/mysql/scopes.sql @@ -10,13 +10,13 @@ START TRANSACTION; -- Insert scope information into the temporary tables. -- -INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES - ('openid', 'log in using your identity', 'user', false, true, false, null), - ('profile', 'basic profile information', 'list-alt', false, true, false, null), - ('email', 'email address', 'envelope', false, true, false, null), - ('address', 'physical address', 'home', false, true, false, null), - ('phone', 'telephone number', 'bell', false, true, false, null), - ('offline_access', 'offline access', 'time', false, false, false, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope) VALUES + ('openid', 'log in using your identity', 'user', false, true), + ('profile', 'basic profile information', 'list-alt', false, true), + ('email', 'email address', 'envelope', false, true), + ('address', 'physical address', 'home', false, true), + ('phone', 'telephone number', 'bell', false, true), + ('offline_access', 'offline access', 'time', false, false); -- -- Merge the temporary scopes safely into the database. This is a two-step process to keep scopes from being created on every startup with a persistent store. @@ -28,4 +28,4 @@ INSERT INTO system_scope (scope, description, icon, restricted, default_scope, s COMMIT; -SET AUTOCOMMIT = 1; \ No newline at end of file +SET AUTOCOMMIT = 1; diff --git a/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql index 5f88b689bf..9f430adace 100644 --- a/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/oracle/oracle_database_tables.sql @@ -255,14 +255,11 @@ CREATE TABLE system_scope ( description VARCHAR2(4000), icon VARCHAR2(256), restricted NUMBER(1) DEFAULT 0 NOT NULL, - default_scope NUMBER(1) DEFAULT 0 NOT NULL, - structured NUMBER(1) DEFAULT 0 NOT NULL, - structured_param_description VARCHAR2(256), + default_scope NUMBER(1) DEFAULT 0 NOT NULL CONSTRAINT system_scope_unique UNIQUE (scope), CONSTRAINT default_scope_check CHECK (default_scope in (1,0)), - CONSTRAINT restricted_check CHECK (restricted in (1,0)), - CONSTRAINT structured_check CHECK (structured in (1,0)) + CONSTRAINT restricted_check CHECK (restricted in (1,0)) ); CREATE SEQUENCE system_scope_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; diff --git a/openid-connect-server-webapp/src/main/resources/db/oracle/scopes_oracle.sql b/openid-connect-server-webapp/src/main/resources/db/oracle/scopes_oracle.sql index 98e98bfcbe..bb6bc82a23 100644 --- a/openid-connect-server-webapp/src/main/resources/db/oracle/scopes_oracle.sql +++ b/openid-connect-server-webapp/src/main/resources/db/oracle/scopes_oracle.sql @@ -2,26 +2,25 @@ -- Insert scope information into the temporary tables. -- -INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES - ('openid', 'log in using your identity', 'user', 0, 1, 0, null); -INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES - ('profile', 'basic profile information', 'list-alt', 0, 1, 0, null); -INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES - ('email', 'email address', 'envelope', 0, 1, 0, null); -INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES - ('address', 'physical address', 'home', 0, 1, 0, null); -INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES - ('phone', 'telephone number', 'bell', 0, 1, 0, null); -INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES - ('offline_access', 'offline access', 'time', 0, 0, 0, null); - +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope) VALUES + ('openid', 'log in using your identity', 'user', 0, 1); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope) VALUES + ('profile', 'basic profile information', 'list-alt', 0, 1); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope) VALUES + ('email', 'email address', 'envelope', 0, 1); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope) VALUES + ('address', 'physical address', 'home', 0, 1); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope) VALUES + ('phone', 'telephone number', 'bell', 0, 1, 0); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope) VALUES + ('offline_access', 'offline access', 'time', 0, 0); -- -- Merge the temporary scopes safely into the database. This is a two-step process to keep scopes from being created on every startup with a persistent store. -- MERGE INTO system_scope - USING (SELECT scope, description, icon, restricted, default_scope, structured, structured_param_description FROM system_scope_TEMP) vals + USING (SELECT scope, description, icon, restricted, default_scope FROM system_scope_TEMP) vals ON (vals.scope = system_scope.scope) WHEN NOT MATCHED THEN - INSERT (id, scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES(system_scope_seq.nextval, vals.scope, - vals.description, vals.icon, vals.restricted, vals.default_scope, vals.structured, vals.structured_param_description); \ No newline at end of file + INSERT (id, scope, description, icon, restricted, default_scope) VALUES(system_scope_seq.nextval, vals.scope, + vals.description, vals.icon, vals.restricted, vals.default_scope); diff --git a/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql index a9992993f8..be871b7e80 100644 --- a/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/psql/psql_database_tables.sql @@ -239,8 +239,6 @@ CREATE TABLE IF NOT EXISTS system_scope ( icon VARCHAR(256), restricted BOOLEAN DEFAULT false NOT NULL, default_scope BOOLEAN DEFAULT false NOT NULL, - structured BOOLEAN DEFAULT false NOT NULL, - structured_param_description VARCHAR(256), UNIQUE (scope) ); diff --git a/openid-connect-server-webapp/src/main/resources/db/psql/scopes.sql b/openid-connect-server-webapp/src/main/resources/db/psql/scopes.sql index 8b2611b832..140c727554 100644 --- a/openid-connect-server-webapp/src/main/resources/db/psql/scopes.sql +++ b/openid-connect-server-webapp/src/main/resources/db/psql/scopes.sql @@ -10,20 +10,20 @@ START TRANSACTION; -- Insert scope information into the temporary tables. -- -INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES - ('openid', 'log in using your identity', 'user', false, true, false, null), - ('profile', 'basic profile information', 'list-alt', false, true, false, null), - ('email', 'email address', 'envelope', false, true, false, null), - ('address', 'physical address', 'home', false, true, false, null), - ('phone', 'telephone number', 'bell', false, true, false, null), - ('offline_access', 'offline access', 'time', false, false, false, null); +INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope) VALUES + ('openid', 'log in using your identity', 'user', false, true), + ('profile', 'basic profile information', 'list-alt', false, true), + ('email', 'email address', 'envelope', false, true), + ('address', 'physical address', 'home', false, true), + ('phone', 'telephone number', 'bell', false, true), + ('offline_access', 'offline access', 'time', false, false); -- -- Merge the temporary scopes safely into the database. This is a two-step process to keep scopes from being created on every startup with a persistent store. -- -INSERT INTO system_scope (scope, description, icon, restricted, default_scope, structured, structured_param_description) - SELECT scope, description, icon, restricted, default_scope, structured, structured_param_description FROM system_scope_TEMP +INSERT INTO system_scope (scope, description, icon, restricted, default_scope) + SELECT scope, description, icon, restricted, default_scope FROM system_scope_TEMP ON CONFLICT(scope) DO NOTHING; From 676451c73d5fe247f7626e749b676abe54c648ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=84=AD=E8=84=88=E9=BE=8D?= Date: Thu, 21 Jun 2018 10:05:49 +0800 Subject: [PATCH 252/269] fix bug #1397 Attempting to execute an operation on a closed EntityManager. --- .../oauth2/repository/impl/JpaOAuth2TokenRepository.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java index 84267023c5..718a233572 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaOAuth2TokenRepository.java @@ -190,7 +190,7 @@ public Set getAccessTokensByUserName(String name) { TypedQuery query = manager.createNamedQuery(OAuth2AccessTokenEntity.QUERY_BY_NAME, OAuth2AccessTokenEntity.class); query.setParameter(OAuth2AccessTokenEntity.PARAM_NAME, name); List results = query.getResultList(); - return results != null ? new HashSet<>(query.getResultList()) : new HashSet<>(); + return results != null ? new HashSet<>(results) : new HashSet<>(); } @Override @@ -198,7 +198,7 @@ public Set getRefreshTokensByUserName(String name) { TypedQuery query = manager.createNamedQuery(OAuth2RefreshTokenEntity.QUERY_BY_NAME, OAuth2RefreshTokenEntity.class); query.setParameter(OAuth2RefreshTokenEntity.PARAM_NAME, name); List results = query.getResultList(); - return results != null ? new HashSet<>(query.getResultList()) : new HashSet<>(); + return results != null ? new HashSet<>(results) : new HashSet<>(); } @Override From 4979f9f50eaddc22a1fb8f3adf9025439f82a955 Mon Sep 17 00:00:00 2001 From: Brady Mulhollem Date: Tue, 27 Nov 2018 16:06:38 -0500 Subject: [PATCH 253/269] #1435: Update guava dependency to latest version. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4a746a4253..580e4a5745 100644 --- a/pom.xml +++ b/pom.xml @@ -564,7 +564,7 @@ com.google.guava guava - 21.0 + 27.0-jre com.google.code.gson From 8430b42ab3e3ada9bc20d6fda1f4e1670749c27f Mon Sep 17 00:00:00 2001 From: Stephen Moore Date: Mon, 12 Nov 2018 09:48:50 -0500 Subject: [PATCH 254/269] Both approve pages were using pagecontext rather than the configured issuer --- .../src/main/webapp/WEB-INF/views/approve.jsp | 2 +- .../src/main/webapp/WEB-INF/views/approveDevice.jsp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approve.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approve.jsp index 4d3dda8ce7..6526fb8426 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approve.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approve.jsp @@ -37,7 +37,7 @@ + action="${ config.issuer }${ config.issuer.endsWith('/') ? '' : '/' }authorize" method="post">
    diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approveDevice.jsp b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approveDevice.jsp index c49e1e8741..162170311e 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approveDevice.jsp +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/views/approveDevice.jsp @@ -37,7 +37,7 @@ + action="${ config.issuer }${ config.issuer.endsWith('/') ? '' : '/' }device/approve" method="post">
    From ad64aef0c58545a034cb855950d763495235efb3 Mon Sep 17 00:00:00 2001 From: Martin Kuba Date: Wed, 6 Mar 2019 11:35:05 +0100 Subject: [PATCH 255/269] updated dependencies patchlevels --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 580e4a5745..94d61d2c95 100644 --- a/pom.xml +++ b/pom.xml @@ -365,7 +365,7 @@ org.springframework spring-framework-bom - 4.3.7.RELEASE + 4.3.22.RELEASE pom import @@ -374,19 +374,19 @@ com.fasterxml.jackson.core jackson-databind - 2.9.0.pr2 + 2.9.8 com.fasterxml.jackson.core jackson-annotations - 2.9.0.pr2 + 2.9.8 org.springframework.security spring-security-bom - 4.2.4.RELEASE + 4.2.11.RELEASE pom import From ae7debba2f44f84ee638cab0ac8b35335c347ba2 Mon Sep 17 00:00:00 2001 From: Martin Kuba Date: Wed, 6 Mar 2019 11:14:52 +0100 Subject: [PATCH 256/269] added refresh_token into grant_types_supported --- .../main/java/org/mitre/discovery/web/DiscoveryEndpoint.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java b/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java index 47e9b20741..270a7649eb 100644 --- a/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java @@ -304,7 +304,7 @@ OPTIONAL. JSON array containing a list of the JWS signing algorithms (alg values JWSAlgorithm.ES256, JWSAlgorithm.ES384, JWSAlgorithm.ES512, JWSAlgorithm.PS256, JWSAlgorithm.PS384, JWSAlgorithm.PS512, Algorithm.NONE); - ArrayList grantTypes = Lists.newArrayList("authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer", "client_credentials", "urn:ietf:params:oauth:grant_type:redelegate", "urn:ietf:params:oauth:grant-type:device_code"); + ArrayList grantTypes = Lists.newArrayList("authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer", "client_credentials", "urn:ietf:params:oauth:grant_type:redelegate", "urn:ietf:params:oauth:grant-type:device_code","refresh_token"); Map m = new HashMap<>(); m.put("issuer", config.getIssuer()); From 5aa8b2a0a77baa937f3c9eaa757b25420e17a7ae Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 19 Apr 2019 16:00:06 -0400 Subject: [PATCH 257/269] updated changelog for release --- CHANGELOG.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5609bc7045..96c6356a0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ Unreleased: + +*1.3.3*: - Authorization codes are now longer - Client/RS can parse the "sub" and "user_id" claims in introspection response - Database-direct queries for fetching tokens by user (optimization) @@ -6,9 +8,12 @@ Unreleased: - Long scopes display properly and are still checkable - Language system remebers when it can't find a file and stops throwing so many errors - Index added for refresh tokens -- Updated to Spring Security 4.2.4 +- Updated to Spring Security 4.2.11 +- Updated Spring to 4.3.22 +- Change approve pages to use issuer instead of page context +- Updated oracle database scripts -*1.3.2: +*1.3.2*: - Added changelog - Set default redirect URI resolver strict matching to true - Fixed XSS vulnerability on redirect URI display on approval page From 73459f0348135013952146196d94e0d065f62739 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 19 Apr 2019 16:04:40 -0400 Subject: [PATCH 258/269] [maven-release-plugin] prepare release mitreid-connect-1.3.3 --- openid-connect-client/pom.xml | 2 +- openid-connect-common/pom.xml | 2 +- openid-connect-server-webapp/pom.xml | 2 +- openid-connect-server/pom.xml | 2 +- pom.xml | 2 +- uma-server-webapp/pom.xml | 2 +- uma-server/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml index 5e5bfae316..1612f52365 100644 --- a/openid-connect-client/pom.xml +++ b/openid-connect-client/pom.xml @@ -22,7 +22,7 @@ openid-connect-parent org.mitre - 1.3.3-SNAPSHOT + 1.3.3 .. openid-connect-client diff --git a/openid-connect-common/pom.xml b/openid-connect-common/pom.xml index 0cc1c23f38..3c27e0a664 100644 --- a/openid-connect-common/pom.xml +++ b/openid-connect-common/pom.xml @@ -22,7 +22,7 @@ openid-connect-parent org.mitre - 1.3.3-SNAPSHOT + 1.3.3 .. openid-connect-common diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index 9a4c45bc3d..500eba9744 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -21,7 +21,7 @@ org.mitre openid-connect-parent - 1.3.3-SNAPSHOT + 1.3.3 openid-connect-server-webapp war diff --git a/openid-connect-server/pom.xml b/openid-connect-server/pom.xml index ff6c36d088..936ba73ab9 100644 --- a/openid-connect-server/pom.xml +++ b/openid-connect-server/pom.xml @@ -23,7 +23,7 @@ org.mitre openid-connect-parent - 1.3.3-SNAPSHOT + 1.3.3 .. diff --git a/pom.xml b/pom.xml index 94d61d2c95..c5fc592f1a 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.mitre openid-connect-parent - 1.3.3-SNAPSHOT + 1.3.3 MITREid Connect pom diff --git a/uma-server-webapp/pom.xml b/uma-server-webapp/pom.xml index 25bf84abd5..acf028d569 100644 --- a/uma-server-webapp/pom.xml +++ b/uma-server-webapp/pom.xml @@ -19,7 +19,7 @@ org.mitre openid-connect-parent - 1.3.3-SNAPSHOT + 1.3.3 .. uma-server-webapp diff --git a/uma-server/pom.xml b/uma-server/pom.xml index a1ffeb1a93..31d127d275 100644 --- a/uma-server/pom.xml +++ b/uma-server/pom.xml @@ -19,7 +19,7 @@ org.mitre openid-connect-parent - 1.3.3-SNAPSHOT + 1.3.3 .. uma-server From 621e86e62dfe83bffc51342f874a579381e7f2bd Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 19 Apr 2019 16:04:45 -0400 Subject: [PATCH 259/269] [maven-release-plugin] prepare for next development iteration --- openid-connect-client/pom.xml | 2 +- openid-connect-common/pom.xml | 2 +- openid-connect-server-webapp/pom.xml | 2 +- openid-connect-server/pom.xml | 2 +- pom.xml | 2 +- uma-server-webapp/pom.xml | 2 +- uma-server/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml index 1612f52365..b891d4e0e6 100644 --- a/openid-connect-client/pom.xml +++ b/openid-connect-client/pom.xml @@ -22,7 +22,7 @@ openid-connect-parent org.mitre - 1.3.3 + 1.3.4-SNAPSHOT .. openid-connect-client diff --git a/openid-connect-common/pom.xml b/openid-connect-common/pom.xml index 3c27e0a664..a2b88b5179 100644 --- a/openid-connect-common/pom.xml +++ b/openid-connect-common/pom.xml @@ -22,7 +22,7 @@ openid-connect-parent org.mitre - 1.3.3 + 1.3.4-SNAPSHOT .. openid-connect-common diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index 500eba9744..11881547f7 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -21,7 +21,7 @@ org.mitre openid-connect-parent - 1.3.3 + 1.3.4-SNAPSHOT openid-connect-server-webapp war diff --git a/openid-connect-server/pom.xml b/openid-connect-server/pom.xml index 936ba73ab9..84fe62198e 100644 --- a/openid-connect-server/pom.xml +++ b/openid-connect-server/pom.xml @@ -23,7 +23,7 @@ org.mitre openid-connect-parent - 1.3.3 + 1.3.4-SNAPSHOT .. diff --git a/pom.xml b/pom.xml index c5fc592f1a..4ebd49e5a7 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.mitre openid-connect-parent - 1.3.3 + 1.3.4-SNAPSHOT MITREid Connect pom diff --git a/uma-server-webapp/pom.xml b/uma-server-webapp/pom.xml index acf028d569..e0485e03a5 100644 --- a/uma-server-webapp/pom.xml +++ b/uma-server-webapp/pom.xml @@ -19,7 +19,7 @@ org.mitre openid-connect-parent - 1.3.3 + 1.3.4-SNAPSHOT .. uma-server-webapp diff --git a/uma-server/pom.xml b/uma-server/pom.xml index 31d127d275..2373d34c1c 100644 --- a/uma-server/pom.xml +++ b/uma-server/pom.xml @@ -19,7 +19,7 @@ org.mitre openid-connect-parent - 1.3.3 + 1.3.4-SNAPSHOT .. uma-server From cc6bd4b5900bb14fef56f2bd212af2e8d8a5809b Mon Sep 17 00:00:00 2001 From: ruslan Date: Wed, 10 Apr 2019 17:13:20 +0300 Subject: [PATCH 260/269] upgrade eclipselink to v. 2.7.4 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 4ebd49e5a7..6824f13603 100644 --- a/pom.xml +++ b/pom.xml @@ -439,12 +439,12 @@ org.eclipse.persistence org.eclipse.persistence.jpa - 2.5.1 + 2.7.4 org.eclipse.persistence javax.persistence - 2.1.1 + 2.2.1 com.zaxxer @@ -595,7 +595,7 @@ org.eclipse.persistence org.eclipse.persistence.core - 2.5.1 + 2.7.4 org.apache.commons From 0d4ef2cb4f77bea5df9e2d4f1cfff4dffb7045c0 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Thu, 1 Aug 2019 16:41:55 -0400 Subject: [PATCH 261/269] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index febfbd6737..610579f550 100644 --- a/README.md +++ b/README.md @@ -28,4 +28,4 @@ The authors and key contributors of the project include: * [Mark Janssen](https://github.com/praseodym) -Copyright ©2018, [MIT Internet Trust Consortium](http://www.trust.mit.edu/). Licensed under the Apache 2.0 license, for details see `LICENSE.txt`. +Licensed under the Apache 2.0 license, for details see `LICENSE.txt`. From 7eba3c12fed82388f917e8dd9b73e86e3a311e4c Mon Sep 17 00:00:00 2001 From: Michael Stepankin Date: Fri, 12 Feb 2021 15:22:12 +0000 Subject: [PATCH 262/269] Fix Spring Autobinding vulnerability 1. Make authorizationRequest no longer affected by http request parameters due to @ModelAttribute. See http://agrrrdog.blogspot.com/2017/03/autobinding-vulns-and-spring-mvc.html --- .../org/mitre/oauth2/web/OAuthConfirmationController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuthConfirmationController.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuthConfirmationController.java index d89464690c..29c9a1419e 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuthConfirmationController.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/OAuthConfirmationController.java @@ -103,9 +103,9 @@ public OAuthConfirmationController(ClientDetailsEntityService clientService) { @PreAuthorize("hasRole('ROLE_USER')") @RequestMapping("/oauth/confirm_access") - public String confimAccess(Map model, @ModelAttribute("authorizationRequest") AuthorizationRequest authRequest, - Principal p) { + public String confirmAccess(Map model, Principal p) { + AuthorizationRequest authRequest = (AuthorizationRequest) model.get("authorizationRequest"); // Check the "prompt" parameter to see if we need to do special processing String prompt = (String)authRequest.getExtensions().get(PROMPT); From 6906f616e2b6962c6a5179df2e79f5b2c31880ab Mon Sep 17 00:00:00 2001 From: shrexster42 Date: Sat, 18 Dec 2021 22:30:43 +0000 Subject: [PATCH 263/269] Upgrade to Java 11 and Spring 5 --- openid-connect-common/pom.xml | 20 +++++++ .../UriEncodedClientUserDetailsService.java | 2 +- .../webapp/WEB-INF/application-context.xml | 22 ++++--- .../main/webapp/WEB-INF/assertion-config.xml | 10 ++-- .../src/main/webapp/WEB-INF/authz-config.xml | 10 ++-- .../src/main/webapp/WEB-INF/crypto-config.xml | 10 ++-- .../src/main/webapp/WEB-INF/data-context.xml | 4 +- .../main/webapp/WEB-INF/endpoint-config.xml | 10 ++-- .../src/main/webapp/WEB-INF/jpa-config.xml | 10 ++-- .../src/main/webapp/WEB-INF/local-config.xml | 10 ++-- .../src/main/webapp/WEB-INF/server-config.xml | 10 ++-- .../main/webapp/WEB-INF/spring-servlet.xml | 10 ++-- .../src/main/webapp/WEB-INF/task-config.xml | 4 +- .../src/main/webapp/WEB-INF/ui-config.xml | 10 ++-- .../src/main/webapp/WEB-INF/user-context.xml | 11 ++-- .../DynamicClientRegistrationEndpoint.java | 26 ++------- ...ProtectedResourceRegistrationEndpoint.java | 33 +++-------- .../impl/TestMITREidDataService_1_0.java | 40 ++++++------- .../impl/TestMITREidDataService_1_1.java | 40 ++++++------- .../impl/TestMITREidDataService_1_2.java | 40 ++++++------- .../impl/TestMITREidDataService_1_3.java | 58 +++++++++---------- pom.xml | 41 ++++++++++--- 22 files changed, 228 insertions(+), 203 deletions(-) diff --git a/openid-connect-common/pom.xml b/openid-connect-common/pom.xml index a2b88b5179..ebe5b462d9 100644 --- a/openid-connect-common/pom.xml +++ b/openid-connect-common/pom.xml @@ -87,6 +87,26 @@ org.bouncycastle bcprov-jdk15on + + javax.annotation + javax.annotation-api + + + jakarta.xml.bind + jakarta.xml.bind-api + + + javax.xml.bind + jaxb-api + + + javax.activation + activation + + + org.glassfish.jaxb + jaxb-runtime + jar diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java index 335efbf184..64ef7e45cf 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java @@ -91,7 +91,7 @@ public UserDetails loadUserByUsername(String clientId) throws UsernameNotFoundE } else { throw new UsernameNotFoundException("Client not found: " + clientId); } - } catch (UnsupportedEncodingException | InvalidClientException e) { + } catch (InvalidClientException e) { throw new UsernameNotFoundException("Client not found: " + clientId); } diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml index 480b5780ca..fdbc37ba72 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml @@ -25,12 +25,12 @@ xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd - http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> @@ -246,9 +246,15 @@ + + - - + + + + + + diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/assertion-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/assertion-config.xml index 0ec4ce7f68..59ea49fe90 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/assertion-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/assertion-config.xml @@ -22,11 +22,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml index 3b7a4faa87..0c5e5019f8 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/authz-config.xml @@ -22,11 +22,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml index 67d8bd146b..0313b5b1b5 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/data-context.xml @@ -19,8 +19,8 @@ + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/endpoint-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/endpoint-config.xml index 14fbcf2ea7..bcfc14a6c3 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/endpoint-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/endpoint-config.xml @@ -22,11 +22,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml index 592d56a2e9..afe40844af 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/jpa-config.xml @@ -22,11 +22,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/local-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/local-config.xml index 3e5fef8e8a..e580f6e52a 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/local-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/local-config.xml @@ -24,11 +24,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/server-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/server-config.xml index 544f01c98b..bf9f998652 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/server-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/server-config.xml @@ -24,11 +24,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/spring-servlet.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/spring-servlet.xml index 9306834d02..f37e980ba6 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/spring-servlet.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/spring-servlet.xml @@ -24,11 +24,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/task-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/task-config.xml index 2b75133281..30f3e98a9a 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/task-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/task-config.xml @@ -19,8 +19,8 @@ + xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/ui-config.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/ui-config.xml index 99951d2306..2e0cf646bb 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/ui-config.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/ui-config.xml @@ -22,11 +22,11 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml index 2aff943637..1bd665d398 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/user-context.xml @@ -24,15 +24,16 @@ xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd - http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd - http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd - http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> + http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> + diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DynamicClientRegistrationEndpoint.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DynamicClientRegistrationEndpoint.java index a96f8209ea..79ea68116c 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DynamicClientRegistrationEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/DynamicClientRegistrationEndpoint.java @@ -246,10 +246,6 @@ public String registerNewClient(@RequestBody String jsonString, Model m) { m.addAttribute(HttpCodeView.CODE, HttpStatus.CREATED); // http 201 return ClientInformationResponseView.VIEWNAME; - } catch (UnsupportedEncodingException e) { - logger.error("Unsupported encoding", e); - m.addAttribute(HttpCodeView.CODE, HttpStatus.INTERNAL_SERVER_ERROR); - return HttpCodeView.VIEWNAME; } catch (IllegalArgumentException e) { logger.error("Couldn't save client", e); @@ -284,20 +280,14 @@ public String readClientConfiguration(@PathVariable("id") String clientId, Model if (client != null && client.getClientId().equals(auth.getOAuth2Request().getClientId())) { - try { - OAuth2AccessTokenEntity token = rotateRegistrationTokenIfNecessary(auth, client); - RegisteredClient registered = new RegisteredClient(client, token.getValue(), config.getIssuer() + "register/" + UriUtils.encodePathSegment(client.getClientId(), "UTF-8")); + OAuth2AccessTokenEntity token = rotateRegistrationTokenIfNecessary(auth, client); + RegisteredClient registered = new RegisteredClient(client, token.getValue(), config.getIssuer() + "register/" + UriUtils.encodePathSegment(client.getClientId(), "UTF-8")); - // send it all out to the view - m.addAttribute("client", registered); - m.addAttribute(HttpCodeView.CODE, HttpStatus.OK); // http 200 + // send it all out to the view + m.addAttribute("client", registered); + m.addAttribute(HttpCodeView.CODE, HttpStatus.OK); // http 200 - return ClientInformationResponseView.VIEWNAME; - } catch (UnsupportedEncodingException e) { - logger.error("Unsupported encoding", e); - m.addAttribute(HttpCodeView.CODE, HttpStatus.INTERNAL_SERVER_ERROR); - return HttpCodeView.VIEWNAME; - } + return ClientInformationResponseView.VIEWNAME; } else { // client mismatch @@ -382,10 +372,6 @@ public String updateClient(@PathVariable("id") String clientId, @RequestBody Str m.addAttribute(HttpCodeView.CODE, HttpStatus.OK); // http 200 return ClientInformationResponseView.VIEWNAME; - } catch (UnsupportedEncodingException e) { - logger.error("Unsupported encoding", e); - m.addAttribute(HttpCodeView.CODE, HttpStatus.INTERNAL_SERVER_ERROR); - return HttpCodeView.VIEWNAME; } catch (IllegalArgumentException e) { logger.error("Couldn't save client", e); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ProtectedResourceRegistrationEndpoint.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ProtectedResourceRegistrationEndpoint.java index 9e2e89b336..5be40caa34 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ProtectedResourceRegistrationEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/ProtectedResourceRegistrationEndpoint.java @@ -177,10 +177,6 @@ public String registerNewProtectedResource(@RequestBody String jsonString, Model m.addAttribute(HttpCodeView.CODE, HttpStatus.CREATED); // http 201 return ClientInformationResponseView.VIEWNAME; - } catch (UnsupportedEncodingException e) { - logger.error("Unsupported encoding", e); - m.addAttribute(HttpCodeView.CODE, HttpStatus.INTERNAL_SERVER_ERROR); - return HttpCodeView.VIEWNAME; } catch (IllegalArgumentException e) { logger.error("Couldn't save client", e); @@ -231,25 +227,18 @@ public String readResourceConfiguration(@PathVariable("id") String clientId, Mod ClientDetailsEntity client = clientService.loadClientByClientId(clientId); if (client != null && client.getClientId().equals(auth.getOAuth2Request().getClientId())) { + + // possibly update the token + OAuth2AccessTokenEntity token = fetchValidRegistrationToken(auth, client); + RegisteredClient registered = new RegisteredClient(client, token.getValue(), config.getIssuer() + "resource/" + UriUtils.encodePathSegment(client.getClientId(), "UTF-8")); + // send it all out to the view + m.addAttribute("client", registered); + m.addAttribute(HttpCodeView.CODE, HttpStatus.OK); // http 200 - try { - // possibly update the token - OAuth2AccessTokenEntity token = fetchValidRegistrationToken(auth, client); - - RegisteredClient registered = new RegisteredClient(client, token.getValue(), config.getIssuer() + "resource/" + UriUtils.encodePathSegment(client.getClientId(), "UTF-8")); - - // send it all out to the view - m.addAttribute("client", registered); - m.addAttribute(HttpCodeView.CODE, HttpStatus.OK); // http 200 - - return ClientInformationResponseView.VIEWNAME; - } catch (UnsupportedEncodingException e) { - logger.error("Unsupported encoding", e); - m.addAttribute(HttpCodeView.CODE, HttpStatus.INTERNAL_SERVER_ERROR); - return HttpCodeView.VIEWNAME; - } + return ClientInformationResponseView.VIEWNAME; + } else { // client mismatch logger.error("readResourceConfiguration failed, client ID mismatch: " @@ -356,10 +345,6 @@ public String updateProtectedResource(@PathVariable("id") String clientId, @Requ m.addAttribute(HttpCodeView.CODE, HttpStatus.OK); // http 200 return ClientInformationResponseView.VIEWNAME; - } catch (UnsupportedEncodingException e) { - logger.error("Unsupported encoding", e); - m.addAttribute(HttpCodeView.CODE, HttpStatus.INTERNAL_SERVER_ERROR); - return HttpCodeView.VIEWNAME; } catch (IllegalArgumentException e) { logger.error("Couldn't save client", e); diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java index 6d5e7ec7ca..74c275939f 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_0.java @@ -144,7 +144,7 @@ public int compare(OAuth2RefreshTokenEntity entity1, OAuth2RefreshTokenEntity en @Test public void testImportRefreshTokens() throws IOException, ParseException { - Date expirationDate1 = formatter.parse("2014-09-10T22:49:44.090+0000", Locale.ENGLISH); + Date expirationDate1 = formatter.parse("2014-09-10T22:49:44.090+00:00", Locale.ENGLISH); ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); when(mockedClient1.getClientId()).thenReturn("mocked_client_1"); @@ -159,7 +159,7 @@ public void testImportRefreshTokens() throws IOException, ParseException { token1.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.")); token1.setAuthenticationHolder(mockedAuthHolder1); - Date expirationDate2 = formatter.parse("2015-01-07T18:31:50.079+0000", Locale.ENGLISH); + Date expirationDate2 = formatter.parse("2015-01-07T18:31:50.079+00:00", Locale.ENGLISH); ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); when(mockedClient2.getClientId()).thenReturn("mocked_client_2"); @@ -184,9 +184,9 @@ public void testImportRefreshTokens() throws IOException, ParseException { "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + "\"" + MITREidDataService.REFRESHTOKENS + "\": [" + - "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+00:00\"," + "\"authenticationHolderId\":1,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.\"}," + - "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+0000\"," + "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+00:00\"," + "\"authenticationHolderId\":2,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJlYmEyYjc3My0xNjAzLTRmNDAtOWQ3MS1hMGIxZDg1OWE2MDAifQ.\"}" + " ]" + @@ -261,7 +261,7 @@ public int compare(OAuth2AccessTokenEntity entity1, OAuth2AccessTokenEntity enti @Test public void testImportAccessTokens() throws IOException, ParseException { - Date expirationDate1 = formatter.parse("2014-09-10T22:49:44.090+0000", Locale.ENGLISH); + Date expirationDate1 = formatter.parse("2014-09-10T22:49:44.090+00:00", Locale.ENGLISH); ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); when(mockedClient1.getClientId()).thenReturn("mocked_client_1"); @@ -278,7 +278,7 @@ public void testImportAccessTokens() throws IOException, ParseException { token1.setScope(ImmutableSet.of("id-token")); token1.setTokenType("Bearer"); - String expiration2 = "2015-01-07T18:31:50.079+0000"; + String expiration2 = "2015-01-07T18:31:50.079+00:00"; Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); @@ -310,10 +310,10 @@ public void testImportAccessTokens() throws IOException, ParseException { "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + "\"" + MITREidDataService.ACCESSTOKENS + "\": [" + - "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+00:00\"," + "\"refreshTokenId\":null,\"idTokenId\":null,\"scope\":[\"id-token\"],\"type\":\"Bearer\"," + "\"authenticationHolderId\":1,\"value\":\"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3ODk5NjgsInN1YiI6IjkwMzQyLkFTREZKV0ZBIiwiYXRfaGFzaCI6InptTmt1QmNRSmNYQktNaVpFODZqY0EiLCJhdWQiOlsiY2xpZW50Il0sImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdDo4MDgwXC9vcGVuaWQtY29ubmVjdC1zZXJ2ZXItd2ViYXBwXC8iLCJpYXQiOjE0MTI3ODkzNjh9.xkEJ9IMXpH7qybWXomfq9WOOlpGYnrvGPgey9UQ4GLzbQx7JC0XgJK83PmrmBZosvFPCmota7FzI_BtwoZLgAZfFiH6w3WIlxuogoH-TxmYbxEpTHoTsszZppkq9mNgOlArV4jrR9y3TPo4MovsH71dDhS_ck-CvAlJunHlqhs0\"}," + - "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+0000\"," + "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+00:00\"," + "\"refreshTokenId\":1,\"idTokenId\":1,\"scope\":[\"openid\",\"offline_access\",\"email\",\"profile\"],\"type\":\"Bearer\"," + "\"authenticationHolderId\":2,\"value\":\"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3OTI5NjgsImF1ZCI6WyJjbGllbnQiXSwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwODBcL29wZW5pZC1jb25uZWN0LXNlcnZlci13ZWJhcHBcLyIsImp0aSI6IjBmZGE5ZmRiLTYyYzItNGIzZS05OTdiLWU0M2VhMDUwMzNiOSIsImlhdCI6MTQxMjc4OTM2OH0.xgaVpRLYE5MzbgXfE0tZt823tjAm6Oh3_kdR1P2I9jRLR6gnTlBQFlYi3Y_0pWNnZSerbAE8Tn6SJHZ9k-curVG0-ByKichV7CNvgsE5X_2wpEaUzejvKf8eZ-BammRY-ie6yxSkAarcUGMvGGOLbkFcz5CtrBpZhfd75J49BIQ\"}" + @@ -576,8 +576,8 @@ public WhitelistedSite answer(InvocationOnMock invocation) throws Throwable { @Test public void testImportGrants() throws IOException, ParseException { - Date creationDate1 = formatter.parse("2014-09-10T22:49:44.090+0000", Locale.ENGLISH); - Date accessDate1 = formatter.parse("2014-09-10T23:49:44.090+0000", Locale.ENGLISH); + Date creationDate1 = formatter.parse("2014-09-10T22:49:44.090+00:00", Locale.ENGLISH); + Date accessDate1 = formatter.parse("2014-09-10T23:49:44.090+00:00", Locale.ENGLISH); OAuth2AccessTokenEntity mockToken1 = mock(OAuth2AccessTokenEntity.class); when(mockToken1.getId()).thenReturn(1L); @@ -591,9 +591,9 @@ public void testImportGrants() throws IOException, ParseException { site1.setAllowedScopes(ImmutableSet.of("openid", "phone")); when(mockToken1.getApprovedSite()).thenReturn(site1); - Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+0000", Locale.ENGLISH); - Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+0000", Locale.ENGLISH); - Date timeoutDate2 = formatter.parse("2014-10-01T20:49:44.090+0000", Locale.ENGLISH); + Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+00:00", Locale.ENGLISH); + Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+00:00", Locale.ENGLISH); + Date timeoutDate2 = formatter.parse("2014-10-01T20:49:44.090+00:00", Locale.ENGLISH); ApprovedSite site2 = new ApprovedSite(); site2.setId(2L); @@ -614,11 +614,11 @@ public void testImportGrants() throws IOException, ParseException { "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + "\"" + MITREidDataService.GRANTS + "\": [" + - "{\"id\":1,\"clientId\":\"foo\",\"creationDate\":\"2014-09-10T22:49:44.090+0000\",\"accessDate\":\"2014-09-10T23:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"foo\",\"creationDate\":\"2014-09-10T22:49:44.090+00:00\",\"accessDate\":\"2014-09-10T23:49:44.090+00:00\"," + "\"userId\":\"user1\",\"whitelistedSiteId\":null,\"allowedScopes\":[\"openid\",\"phone\"], \"whitelistedSiteId\":1," + "\"approvedAccessTokens\":[1]}," + - "{\"id\":2,\"clientId\":\"bar\",\"creationDate\":\"2014-09-11T18:49:44.090+0000\",\"accessDate\":\"2014-09-11T20:49:44.090+0000\"," - + "\"timeoutDate\":\"2014-10-01T20:49:44.090+0000\",\"userId\":\"user2\"," + "{\"id\":2,\"clientId\":\"bar\",\"creationDate\":\"2014-09-11T18:49:44.090+00:00\",\"accessDate\":\"2014-09-11T20:49:44.090+00:00\"," + + "\"timeoutDate\":\"2014-10-01T20:49:44.090+00:00\",\"userId\":\"user2\"," + "\"allowedScopes\":[\"openid\",\"offline_access\",\"email\",\"profile\"]}" + " ]" + @@ -831,7 +831,7 @@ public void testImportSystemScopes() throws IOException { @Test public void testFixRefreshTokenAuthHolderReferencesOnImport() throws IOException, ParseException { - String expiration1 = "2014-09-10T22:49:44.090+0000"; + String expiration1 = "2014-09-10T22:49:44.090+00:00"; Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); @@ -854,7 +854,7 @@ public void testFixRefreshTokenAuthHolderReferencesOnImport() throws IOException token1.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.")); token1.setAuthenticationHolder(holder1); - String expiration2 = "2015-01-07T18:31:50.079+0000"; + String expiration2 = "2015-01-07T18:31:50.079+00:00"; Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); @@ -893,9 +893,9 @@ public void testFixRefreshTokenAuthHolderReferencesOnImport() throws IOException " ]," + "\"" + MITREidDataService.REFRESHTOKENS + "\": [" + - "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+00:00\"," + "\"authenticationHolderId\":1,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.\"}," + - "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+0000\"," + "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+00:00\"," + "\"authenticationHolderId\":2,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJlYmEyYjc3My0xNjAzLTRmNDAtOWQ3MS1hMGIxZDg1OWE2MDAifQ.\"}" + " ]" + diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java index d7ab851fd4..cfeb43a6f7 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_1.java @@ -145,7 +145,7 @@ public int compare(OAuth2RefreshTokenEntity entity1, OAuth2RefreshTokenEntity en @Test public void testImportRefreshTokens() throws IOException, ParseException { - String expiration1 = "2014-09-10T22:49:44.090+0000"; + String expiration1 = "2014-09-10T22:49:44.090+00:00"; Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); @@ -161,7 +161,7 @@ public void testImportRefreshTokens() throws IOException, ParseException { token1.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.")); token1.setAuthenticationHolder(mockedAuthHolder1); - String expiration2 = "2015-01-07T18:31:50.079+0000"; + String expiration2 = "2015-01-07T18:31:50.079+00:00"; Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); @@ -187,9 +187,9 @@ public void testImportRefreshTokens() throws IOException, ParseException { "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + "\"" + MITREidDataService.REFRESHTOKENS + "\": [" + - "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+00:00\"," + "\"authenticationHolderId\":1,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.\"}," + - "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+0000\"," + "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+00:00\"," + "\"authenticationHolderId\":2,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJlYmEyYjc3My0xNjAzLTRmNDAtOWQ3MS1hMGIxZDg1OWE2MDAifQ.\"}" + " ]" + @@ -264,7 +264,7 @@ public int compare(OAuth2AccessTokenEntity entity1, OAuth2AccessTokenEntity enti @Test public void testImportAccessTokens() throws IOException, ParseException { - String expiration1 = "2014-09-10T22:49:44.090+0000"; + String expiration1 = "2014-09-10T22:49:44.090+00:00"; Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); @@ -282,7 +282,7 @@ public void testImportAccessTokens() throws IOException, ParseException { token1.setScope(ImmutableSet.of("id-token")); token1.setTokenType("Bearer"); - String expiration2 = "2015-01-07T18:31:50.079+0000"; + String expiration2 = "2015-01-07T18:31:50.079+00:00"; Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); @@ -314,10 +314,10 @@ public void testImportAccessTokens() throws IOException, ParseException { "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + "\"" + MITREidDataService.ACCESSTOKENS + "\": [" + - "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+00:00\"," + "\"refreshTokenId\":null,\"idTokenId\":null,\"scope\":[\"id-token\"],\"type\":\"Bearer\"," + "\"authenticationHolderId\":1,\"value\":\"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3ODk5NjgsInN1YiI6IjkwMzQyLkFTREZKV0ZBIiwiYXRfaGFzaCI6InptTmt1QmNRSmNYQktNaVpFODZqY0EiLCJhdWQiOlsiY2xpZW50Il0sImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdDo4MDgwXC9vcGVuaWQtY29ubmVjdC1zZXJ2ZXItd2ViYXBwXC8iLCJpYXQiOjE0MTI3ODkzNjh9.xkEJ9IMXpH7qybWXomfq9WOOlpGYnrvGPgey9UQ4GLzbQx7JC0XgJK83PmrmBZosvFPCmota7FzI_BtwoZLgAZfFiH6w3WIlxuogoH-TxmYbxEpTHoTsszZppkq9mNgOlArV4jrR9y3TPo4MovsH71dDhS_ck-CvAlJunHlqhs0\"}," + - "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+0000\"," + "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+00:00\"," + "\"refreshTokenId\":1,\"idTokenId\":1,\"scope\":[\"openid\",\"offline_access\",\"email\",\"profile\"],\"type\":\"Bearer\"," + "\"authenticationHolderId\":2,\"value\":\"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3OTI5NjgsImF1ZCI6WyJjbGllbnQiXSwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwODBcL29wZW5pZC1jb25uZWN0LXNlcnZlci13ZWJhcHBcLyIsImp0aSI6IjBmZGE5ZmRiLTYyYzItNGIzZS05OTdiLWU0M2VhMDUwMzNiOSIsImlhdCI6MTQxMjc4OTM2OH0.xgaVpRLYE5MzbgXfE0tZt823tjAm6Oh3_kdR1P2I9jRLR6gnTlBQFlYi3Y_0pWNnZSerbAE8Tn6SJHZ9k-curVG0-ByKichV7CNvgsE5X_2wpEaUzejvKf8eZ-BammRY-ie6yxSkAarcUGMvGGOLbkFcz5CtrBpZhfd75J49BIQ\"}" + @@ -579,8 +579,8 @@ public WhitelistedSite answer(InvocationOnMock invocation) throws Throwable { @Test public void testImportGrants() throws IOException, ParseException { - Date creationDate1 = formatter.parse("2014-09-10T22:49:44.090+0000", Locale.ENGLISH); - Date accessDate1 = formatter.parse("2014-09-10T23:49:44.090+0000", Locale.ENGLISH); + Date creationDate1 = formatter.parse("2014-09-10T22:49:44.090+00:00", Locale.ENGLISH); + Date accessDate1 = formatter.parse("2014-09-10T23:49:44.090+00:00", Locale.ENGLISH); OAuth2AccessTokenEntity mockToken1 = mock(OAuth2AccessTokenEntity.class); when(mockToken1.getId()).thenReturn(1L); @@ -594,9 +594,9 @@ public void testImportGrants() throws IOException, ParseException { site1.setAllowedScopes(ImmutableSet.of("openid", "phone")); when(mockToken1.getApprovedSite()).thenReturn(site1); - Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+0000", Locale.ENGLISH); - Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+0000", Locale.ENGLISH); - Date timeoutDate2 = formatter.parse("2014-10-01T20:49:44.090+0000", Locale.ENGLISH); + Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+00:00", Locale.ENGLISH); + Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+00:00", Locale.ENGLISH); + Date timeoutDate2 = formatter.parse("2014-10-01T20:49:44.090+00:00", Locale.ENGLISH); ApprovedSite site2 = new ApprovedSite(); site2.setId(2L); @@ -617,11 +617,11 @@ public void testImportGrants() throws IOException, ParseException { "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + "\"" + MITREidDataService.GRANTS + "\": [" + - "{\"id\":1,\"clientId\":\"foo\",\"creationDate\":\"2014-09-10T22:49:44.090+0000\",\"accessDate\":\"2014-09-10T23:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"foo\",\"creationDate\":\"2014-09-10T22:49:44.090+00:00\",\"accessDate\":\"2014-09-10T23:49:44.090+00:00\"," + "\"userId\":\"user1\",\"whitelistedSiteId\":null,\"allowedScopes\":[\"openid\",\"phone\"], \"whitelistedSiteId\":1," + "\"approvedAccessTokens\":[1]}," + - "{\"id\":2,\"clientId\":\"bar\",\"creationDate\":\"2014-09-11T18:49:44.090+0000\",\"accessDate\":\"2014-09-11T20:49:44.090+0000\"," - + "\"timeoutDate\":\"2014-10-01T20:49:44.090+0000\",\"userId\":\"user2\"," + "{\"id\":2,\"clientId\":\"bar\",\"creationDate\":\"2014-09-11T18:49:44.090+00:00\",\"accessDate\":\"2014-09-11T20:49:44.090+00:00\"," + + "\"timeoutDate\":\"2014-10-01T20:49:44.090+00:00\",\"userId\":\"user2\"," + "\"allowedScopes\":[\"openid\",\"offline_access\",\"email\",\"profile\"]}" + " ]" + @@ -833,7 +833,7 @@ public void testImportSystemScopes() throws IOException { @Test public void testFixRefreshTokenAuthHolderReferencesOnImport() throws IOException, ParseException { - String expiration1 = "2014-09-10T22:49:44.090+0000"; + String expiration1 = "2014-09-10T22:49:44.090+00:00"; Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); @@ -856,7 +856,7 @@ public void testFixRefreshTokenAuthHolderReferencesOnImport() throws IOException token1.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.")); token1.setAuthenticationHolder(holder1); - String expiration2 = "2015-01-07T18:31:50.079+0000"; + String expiration2 = "2015-01-07T18:31:50.079+00:00"; Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); @@ -895,9 +895,9 @@ public void testFixRefreshTokenAuthHolderReferencesOnImport() throws IOException " ]," + "\"" + MITREidDataService.REFRESHTOKENS + "\": [" + - "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+00:00\"," + "\"authenticationHolderId\":1,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.\"}," + - "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+0000\"," + "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+00:00\"," + "\"authenticationHolderId\":2,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJlYmEyYjc3My0xNjAzLTRmNDAtOWQ3MS1hMGIxZDg1OWE2MDAifQ.\"}" + " ]" + diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java index 594900ae29..598471126f 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_2.java @@ -147,7 +147,7 @@ public int compare(OAuth2RefreshTokenEntity entity1, OAuth2RefreshTokenEntity en @Test public void testImportRefreshTokens() throws IOException, ParseException { - String expiration1 = "2014-09-10T22:49:44.090+0000"; + String expiration1 = "2014-09-10T22:49:44.090+00:00"; Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); @@ -163,7 +163,7 @@ public void testImportRefreshTokens() throws IOException, ParseException { token1.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.")); token1.setAuthenticationHolder(mockedAuthHolder1); - String expiration2 = "2015-01-07T18:31:50.079+0000"; + String expiration2 = "2015-01-07T18:31:50.079+00:00"; Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); @@ -189,9 +189,9 @@ public void testImportRefreshTokens() throws IOException, ParseException { "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + "\"" + MITREidDataService.REFRESHTOKENS + "\": [" + - "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+00:00\"," + "\"authenticationHolderId\":1,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.\"}," + - "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+0000\"," + "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+00:00\"," + "\"authenticationHolderId\":2,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJlYmEyYjc3My0xNjAzLTRmNDAtOWQ3MS1hMGIxZDg1OWE2MDAifQ.\"}" + " ]" + @@ -266,7 +266,7 @@ public int compare(OAuth2AccessTokenEntity entity1, OAuth2AccessTokenEntity enti @Test public void testImportAccessTokens() throws IOException, ParseException { - String expiration1 = "2014-09-10T22:49:44.090+0000"; + String expiration1 = "2014-09-10T22:49:44.090+00:00"; Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); @@ -284,7 +284,7 @@ public void testImportAccessTokens() throws IOException, ParseException { token1.setScope(ImmutableSet.of("id-token")); token1.setTokenType("Bearer"); - String expiration2 = "2015-01-07T18:31:50.079+0000"; + String expiration2 = "2015-01-07T18:31:50.079+00:00"; Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); @@ -316,10 +316,10 @@ public void testImportAccessTokens() throws IOException, ParseException { "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + "\"" + MITREidDataService.ACCESSTOKENS + "\": [" + - "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+00:00\"," + "\"refreshTokenId\":null,\"idTokenId\":null,\"scope\":[\"id-token\"],\"type\":\"Bearer\"," + "\"authenticationHolderId\":1,\"value\":\"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3ODk5NjgsInN1YiI6IjkwMzQyLkFTREZKV0ZBIiwiYXRfaGFzaCI6InptTmt1QmNRSmNYQktNaVpFODZqY0EiLCJhdWQiOlsiY2xpZW50Il0sImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdDo4MDgwXC9vcGVuaWQtY29ubmVjdC1zZXJ2ZXItd2ViYXBwXC8iLCJpYXQiOjE0MTI3ODkzNjh9.xkEJ9IMXpH7qybWXomfq9WOOlpGYnrvGPgey9UQ4GLzbQx7JC0XgJK83PmrmBZosvFPCmota7FzI_BtwoZLgAZfFiH6w3WIlxuogoH-TxmYbxEpTHoTsszZppkq9mNgOlArV4jrR9y3TPo4MovsH71dDhS_ck-CvAlJunHlqhs0\"}," + - "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+0000\"," + "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+00:00\"," + "\"refreshTokenId\":1,\"idTokenId\":1,\"scope\":[\"openid\",\"offline_access\",\"email\",\"profile\"],\"type\":\"Bearer\"," + "\"authenticationHolderId\":2,\"value\":\"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3OTI5NjgsImF1ZCI6WyJjbGllbnQiXSwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwODBcL29wZW5pZC1jb25uZWN0LXNlcnZlci13ZWJhcHBcLyIsImp0aSI6IjBmZGE5ZmRiLTYyYzItNGIzZS05OTdiLWU0M2VhMDUwMzNiOSIsImlhdCI6MTQxMjc4OTM2OH0.xgaVpRLYE5MzbgXfE0tZt823tjAm6Oh3_kdR1P2I9jRLR6gnTlBQFlYi3Y_0pWNnZSerbAE8Tn6SJHZ9k-curVG0-ByKichV7CNvgsE5X_2wpEaUzejvKf8eZ-BammRY-ie6yxSkAarcUGMvGGOLbkFcz5CtrBpZhfd75J49BIQ\"}" + @@ -581,8 +581,8 @@ public WhitelistedSite answer(InvocationOnMock invocation) throws Throwable { @Test public void testImportGrants() throws IOException, ParseException { - Date creationDate1 = formatter.parse("2014-09-10T22:49:44.090+0000", Locale.ENGLISH); - Date accessDate1 = formatter.parse("2014-09-10T23:49:44.090+0000", Locale.ENGLISH); + Date creationDate1 = formatter.parse("2014-09-10T22:49:44.090+00:00", Locale.ENGLISH); + Date accessDate1 = formatter.parse("2014-09-10T23:49:44.090+00:00", Locale.ENGLISH); OAuth2AccessTokenEntity mockToken1 = mock(OAuth2AccessTokenEntity.class); when(mockToken1.getId()).thenReturn(1L); @@ -596,9 +596,9 @@ public void testImportGrants() throws IOException, ParseException { site1.setAllowedScopes(ImmutableSet.of("openid", "phone")); when(mockToken1.getApprovedSite()).thenReturn(site1); - Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+0000", Locale.ENGLISH); - Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+0000", Locale.ENGLISH); - Date timeoutDate2 = formatter.parse("2014-10-01T20:49:44.090+0000", Locale.ENGLISH); + Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+00:00", Locale.ENGLISH); + Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+00:00", Locale.ENGLISH); + Date timeoutDate2 = formatter.parse("2014-10-01T20:49:44.090+00:00", Locale.ENGLISH); ApprovedSite site2 = new ApprovedSite(); site2.setId(2L); @@ -619,11 +619,11 @@ public void testImportGrants() throws IOException, ParseException { "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + "\"" + MITREidDataService.GRANTS + "\": [" + - "{\"id\":1,\"clientId\":\"foo\",\"creationDate\":\"2014-09-10T22:49:44.090+0000\",\"accessDate\":\"2014-09-10T23:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"foo\",\"creationDate\":\"2014-09-10T22:49:44.090+00:00\",\"accessDate\":\"2014-09-10T23:49:44.090+00:00\"," + "\"userId\":\"user1\",\"whitelistedSiteId\":null,\"allowedScopes\":[\"openid\",\"phone\"], \"whitelistedSiteId\":1," + "\"approvedAccessTokens\":[1]}," + - "{\"id\":2,\"clientId\":\"bar\",\"creationDate\":\"2014-09-11T18:49:44.090+0000\",\"accessDate\":\"2014-09-11T20:49:44.090+0000\"," - + "\"timeoutDate\":\"2014-10-01T20:49:44.090+0000\",\"userId\":\"user2\"," + "{\"id\":2,\"clientId\":\"bar\",\"creationDate\":\"2014-09-11T18:49:44.090+00:00\",\"accessDate\":\"2014-09-11T20:49:44.090+00:00\"," + + "\"timeoutDate\":\"2014-10-01T20:49:44.090+00:00\",\"userId\":\"user2\"," + "\"allowedScopes\":[\"openid\",\"offline_access\",\"email\",\"profile\"]}" + " ]" + @@ -835,7 +835,7 @@ public void testImportSystemScopes() throws IOException { @Test public void testFixRefreshTokenAuthHolderReferencesOnImport() throws IOException, ParseException { - String expiration1 = "2014-09-10T22:49:44.090+0000"; + String expiration1 = "2014-09-10T22:49:44.090+00:00"; Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); @@ -858,7 +858,7 @@ public void testFixRefreshTokenAuthHolderReferencesOnImport() throws IOException token1.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.")); token1.setAuthenticationHolder(holder1); - String expiration2 = "2015-01-07T18:31:50.079+0000"; + String expiration2 = "2015-01-07T18:31:50.079+00:00"; Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); @@ -897,9 +897,9 @@ public void testFixRefreshTokenAuthHolderReferencesOnImport() throws IOException " ]," + "\"" + MITREidDataService.REFRESHTOKENS + "\": [" + - "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+00:00\"," + "\"authenticationHolderId\":1,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.\"}," + - "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+0000\"," + "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+00:00\"," + "\"authenticationHolderId\":2,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJlYmEyYjc3My0xNjAzLTRmNDAtOWQ3MS1hMGIxZDg1OWE2MDAifQ.\"}" + " ]" + diff --git a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java index 29a04d932d..19a12c2350 100644 --- a/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java +++ b/openid-connect-server/src/test/java/org/mitre/openid/connect/service/impl/TestMITREidDataService_1_3.java @@ -150,7 +150,7 @@ public void prepare() { @Test public void testExportRefreshTokens() throws IOException, ParseException { - String expiration1 = "2014-09-10T22:49:44.090+0000"; + String expiration1 = "2014-09-10T22:49:44.090+00:00"; Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); @@ -166,7 +166,7 @@ public void testExportRefreshTokens() throws IOException, ParseException { token1.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.")); token1.setAuthenticationHolder(mockedAuthHolder1); - String expiration2 = "2015-01-07T18:31:50.079+0000"; + String expiration2 = "2015-01-07T18:31:50.079+00:00"; Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); @@ -273,7 +273,7 @@ public int compare(OAuth2RefreshTokenEntity entity1, OAuth2RefreshTokenEntity en @Test public void testImportRefreshTokens() throws IOException, ParseException { - String expiration1 = "2014-09-10T22:49:44.090+0000"; + String expiration1 = "2014-09-10T22:49:44.090+00:00"; Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); @@ -289,7 +289,7 @@ public void testImportRefreshTokens() throws IOException, ParseException { token1.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.")); token1.setAuthenticationHolder(mockedAuthHolder1); - String expiration2 = "2015-01-07T18:31:50.079+0000"; + String expiration2 = "2015-01-07T18:31:50.079+00:00"; Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); @@ -315,9 +315,9 @@ public void testImportRefreshTokens() throws IOException, ParseException { "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + "\"" + MITREidDataService.REFRESHTOKENS + "\": [" + - "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+00:00\"," + "\"authenticationHolderId\":1,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.\"}," + - "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+0000\"," + "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+00:00\"," + "\"authenticationHolderId\":2,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJlYmEyYjc3My0xNjAzLTRmNDAtOWQ3MS1hMGIxZDg1OWE2MDAifQ.\"}" + " ]" + @@ -385,7 +385,7 @@ public AuthenticationHolderEntity answer(InvocationOnMock invocation) throws Thr @Test public void testExportAccessTokens() throws IOException, ParseException { - String expiration1 = "2014-09-10T22:49:44.090+0000"; + String expiration1 = "2014-09-10T22:49:44.090+00:00"; Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); @@ -403,7 +403,7 @@ public void testExportAccessTokens() throws IOException, ParseException { token1.setScope(ImmutableSet.of("id-token")); token1.setTokenType("Bearer"); - String expiration2 = "2015-01-07T18:31:50.079+0000"; + String expiration2 = "2015-01-07T18:31:50.079+00:00"; Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); @@ -523,7 +523,7 @@ public int compare(OAuth2AccessTokenEntity entity1, OAuth2AccessTokenEntity enti @Test public void testImportAccessTokens() throws IOException, ParseException { - String expiration1 = "2014-09-10T22:49:44.090+0000"; + String expiration1 = "2014-09-10T22:49:44.090+00:00"; Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); @@ -541,7 +541,7 @@ public void testImportAccessTokens() throws IOException, ParseException { token1.setScope(ImmutableSet.of("id-token")); token1.setTokenType("Bearer"); - String expiration2 = "2015-01-07T18:31:50.079+0000"; + String expiration2 = "2015-01-07T18:31:50.079+00:00"; Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); @@ -573,10 +573,10 @@ public void testImportAccessTokens() throws IOException, ParseException { "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + "\"" + MITREidDataService.ACCESSTOKENS + "\": [" + - "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+00:00\"," + "\"refreshTokenId\":null,\"idTokenId\":null,\"scope\":[\"id-token\"],\"type\":\"Bearer\"," + "\"authenticationHolderId\":1,\"value\":\"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3ODk5NjgsInN1YiI6IjkwMzQyLkFTREZKV0ZBIiwiYXRfaGFzaCI6InptTmt1QmNRSmNYQktNaVpFODZqY0EiLCJhdWQiOlsiY2xpZW50Il0sImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdDo4MDgwXC9vcGVuaWQtY29ubmVjdC1zZXJ2ZXItd2ViYXBwXC8iLCJpYXQiOjE0MTI3ODkzNjh9.xkEJ9IMXpH7qybWXomfq9WOOlpGYnrvGPgey9UQ4GLzbQx7JC0XgJK83PmrmBZosvFPCmota7FzI_BtwoZLgAZfFiH6w3WIlxuogoH-TxmYbxEpTHoTsszZppkq9mNgOlArV4jrR9y3TPo4MovsH71dDhS_ck-CvAlJunHlqhs0\"}," + - "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+0000\"," + "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+00:00\"," + "\"refreshTokenId\":1,\"idTokenId\":1,\"scope\":[\"openid\",\"offline_access\",\"email\",\"profile\"],\"type\":\"Bearer\"," + "\"authenticationHolderId\":2,\"value\":\"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0MTI3OTI5NjgsImF1ZCI6WyJjbGllbnQiXSwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwODBcL29wZW5pZC1jb25uZWN0LXNlcnZlci13ZWJhcHBcLyIsImp0aSI6IjBmZGE5ZmRiLTYyYzItNGIzZS05OTdiLWU0M2VhMDUwMzNiOSIsImlhdCI6MTQxMjc4OTM2OH0.xgaVpRLYE5MzbgXfE0tZt823tjAm6Oh3_kdR1P2I9jRLR6gnTlBQFlYi3Y_0pWNnZSerbAE8Tn6SJHZ9k-curVG0-ByKichV7CNvgsE5X_2wpEaUzejvKf8eZ-BammRY-ie6yxSkAarcUGMvGGOLbkFcz5CtrBpZhfd75J49BIQ\"}" + @@ -1131,8 +1131,8 @@ public WhitelistedSite answer(InvocationOnMock invocation) throws Throwable { @Test public void testExportGrants() throws IOException, ParseException { - Date creationDate1 = formatter.parse("2014-09-10T22:49:44.090+0000", Locale.ENGLISH); - Date accessDate1 = formatter.parse("2014-09-10T23:49:44.090+0000", Locale.ENGLISH); + Date creationDate1 = formatter.parse("2014-09-10T22:49:44.090+00:00", Locale.ENGLISH); + Date accessDate1 = formatter.parse("2014-09-10T23:49:44.090+00:00", Locale.ENGLISH); OAuth2AccessTokenEntity mockToken1 = mock(OAuth2AccessTokenEntity.class); when(mockToken1.getId()).thenReturn(1L); @@ -1146,9 +1146,9 @@ public void testExportGrants() throws IOException, ParseException { site1.setAllowedScopes(ImmutableSet.of("openid", "phone")); when(mockToken1.getApprovedSite()).thenReturn(site1); - Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+0000", Locale.ENGLISH); - Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+0000", Locale.ENGLISH); - Date timeoutDate2 = formatter.parse("2014-10-01T20:49:44.090+0000", Locale.ENGLISH); + Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+00:00", Locale.ENGLISH); + Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+00:00", Locale.ENGLISH); + Date timeoutDate2 = formatter.parse("2014-10-01T20:49:44.090+00:00", Locale.ENGLISH); ApprovedSite site2 = new ApprovedSite(); site2.setId(2L); @@ -1246,8 +1246,8 @@ public void testExportGrants() throws IOException, ParseException { @Test public void testImportGrants() throws IOException, ParseException { - Date creationDate1 = formatter.parse("2014-09-10T22:49:44.090+0000", Locale.ENGLISH); - Date accessDate1 = formatter.parse("2014-09-10T23:49:44.090+0000", Locale.ENGLISH); + Date creationDate1 = formatter.parse("2014-09-10T22:49:44.090+00:00", Locale.ENGLISH); + Date accessDate1 = formatter.parse("2014-09-10T23:49:44.090+00:00", Locale.ENGLISH); OAuth2AccessTokenEntity mockToken1 = mock(OAuth2AccessTokenEntity.class); when(mockToken1.getId()).thenReturn(1L); @@ -1261,9 +1261,9 @@ public void testImportGrants() throws IOException, ParseException { site1.setAllowedScopes(ImmutableSet.of("openid", "phone")); when(mockToken1.getApprovedSite()).thenReturn(site1); - Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+0000", Locale.ENGLISH); - Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+0000", Locale.ENGLISH); - Date timeoutDate2 = formatter.parse("2014-10-01T20:49:44.090+0000", Locale.ENGLISH); + Date creationDate2 = formatter.parse("2014-09-11T18:49:44.090+00:00", Locale.ENGLISH); + Date accessDate2 = formatter.parse("2014-09-11T20:49:44.090+00:00", Locale.ENGLISH); + Date timeoutDate2 = formatter.parse("2014-10-01T20:49:44.090+00:00", Locale.ENGLISH); ApprovedSite site2 = new ApprovedSite(); site2.setId(2L); @@ -1284,11 +1284,11 @@ public void testImportGrants() throws IOException, ParseException { "\"" + MITREidDataService.AUTHENTICATIONHOLDERS + "\": [], " + "\"" + MITREidDataService.GRANTS + "\": [" + - "{\"id\":1,\"clientId\":\"foo\",\"creationDate\":\"2014-09-10T22:49:44.090+0000\",\"accessDate\":\"2014-09-10T23:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"foo\",\"creationDate\":\"2014-09-10T22:49:44.090+00:00\",\"accessDate\":\"2014-09-10T23:49:44.090+00:00\"," + "\"userId\":\"user1\",\"whitelistedSiteId\":null,\"allowedScopes\":[\"openid\",\"phone\"], \"whitelistedSiteId\":1," + "\"approvedAccessTokens\":[1]}," + - "{\"id\":2,\"clientId\":\"bar\",\"creationDate\":\"2014-09-11T18:49:44.090+0000\",\"accessDate\":\"2014-09-11T20:49:44.090+0000\"," - + "\"timeoutDate\":\"2014-10-01T20:49:44.090+0000\",\"userId\":\"user2\"," + "{\"id\":2,\"clientId\":\"bar\",\"creationDate\":\"2014-09-11T18:49:44.090+00:00\",\"accessDate\":\"2014-09-11T20:49:44.090+00:00\"," + + "\"timeoutDate\":\"2014-10-01T20:49:44.090+00:00\",\"userId\":\"user2\"," + "\"allowedScopes\":[\"openid\",\"offline_access\",\"email\",\"profile\"]}" + " ]" + @@ -1717,7 +1717,7 @@ public void testImportSystemScopes() throws IOException { @Test public void testFixRefreshTokenAuthHolderReferencesOnImport() throws IOException, ParseException { - String expiration1 = "2014-09-10T22:49:44.090+0000"; + String expiration1 = "2014-09-10T22:49:44.090+00:00"; Date expirationDate1 = formatter.parse(expiration1, Locale.ENGLISH); ClientDetailsEntity mockedClient1 = mock(ClientDetailsEntity.class); @@ -1740,7 +1740,7 @@ public void testFixRefreshTokenAuthHolderReferencesOnImport() throws IOException token1.setJwt(JWTParser.parse("eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.")); token1.setAuthenticationHolder(holder1); - String expiration2 = "2015-01-07T18:31:50.079+0000"; + String expiration2 = "2015-01-07T18:31:50.079+00:00"; Date expirationDate2 = formatter.parse(expiration2, Locale.ENGLISH); ClientDetailsEntity mockedClient2 = mock(ClientDetailsEntity.class); @@ -1779,9 +1779,9 @@ public void testFixRefreshTokenAuthHolderReferencesOnImport() throws IOException " ]," + "\"" + MITREidDataService.REFRESHTOKENS + "\": [" + - "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+0000\"," + "{\"id\":1,\"clientId\":\"mocked_client_1\",\"expiration\":\"2014-09-10T22:49:44.090+00:00\"," + "\"authenticationHolderId\":1,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJmOTg4OWQyOS0xMTk1LTQ4ODEtODgwZC1lZjVlYzAwY2Y4NDIifQ.\"}," + - "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+0000\"," + "{\"id\":2,\"clientId\":\"mocked_client_2\",\"expiration\":\"2015-01-07T18:31:50.079+00:00\"," + "\"authenticationHolderId\":2,\"value\":\"eyJhbGciOiJub25lIn0.eyJqdGkiOiJlYmEyYjc3My0xNjAzLTRmNDAtOWQ3MS1hMGIxZDg1OWE2MDAifQ.\"}" + " ]" + diff --git a/pom.xml b/pom.xml index 6824f13603..a3b42f22a3 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,7 @@ - 1.8 + 11 1.7.25 A reference implementation of OpenID Connect (http://openid.net/connect/), OAuth 2.0, and UMA built on top of Java, Spring, and Spring Security. The project contains a fully functioning server, client, and utility library. @@ -96,7 +96,7 @@ org.jacoco jacoco-maven-plugin - 0.7.9 + 0.8.7 org.apache.maven.plugins @@ -178,7 +178,7 @@ ro.isdc.wro4j wro4j-maven-plugin - 1.8.0 + 1.10.0 compile @@ -191,7 +191,7 @@ ro.isdc.wro4j wro4j-extensions - 1.8.0 + 1.10.0 @@ -365,7 +365,7 @@ org.springframework spring-framework-bom - 4.3.22.RELEASE + 5.3.9 pom import @@ -386,7 +386,7 @@ org.springframework.security spring-security-bom - 4.2.11.RELEASE + 5.5.2 pom import @@ -605,7 +605,34 @@ ro.isdc.wro4j wro4j-extensions - 1.8.0 + 1.10.0 + + + + + javax.annotation + javax.annotation-api + 1.3.2 + + + jakarta.xml.bind + jakarta.xml.bind-api + 3.0.0 + + + javax.xml.bind + jaxb-api + 2.3.1 + + + javax.activation + activation + 1.1 + + + org.glassfish.jaxb + jaxb-runtime + 2.3.0-b170127.1453 From a0bd2c70ac75c92f73f6de122e08a4bd155e7774 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 20 Dec 2021 13:09:08 -0500 Subject: [PATCH 264/269] [maven-release-plugin] prepare release mitreid-connect-1.3.4 --- openid-connect-client/pom.xml | 2 +- openid-connect-common/pom.xml | 2 +- openid-connect-server-webapp/pom.xml | 2 +- openid-connect-server/pom.xml | 2 +- pom.xml | 2 +- uma-server-webapp/pom.xml | 2 +- uma-server/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml index b891d4e0e6..e2f9399cb5 100644 --- a/openid-connect-client/pom.xml +++ b/openid-connect-client/pom.xml @@ -22,7 +22,7 @@ openid-connect-parent org.mitre - 1.3.4-SNAPSHOT + 1.3.4 .. openid-connect-client diff --git a/openid-connect-common/pom.xml b/openid-connect-common/pom.xml index a2b88b5179..cab527332f 100644 --- a/openid-connect-common/pom.xml +++ b/openid-connect-common/pom.xml @@ -22,7 +22,7 @@ openid-connect-parent org.mitre - 1.3.4-SNAPSHOT + 1.3.4 .. openid-connect-common diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index 11881547f7..bf2a37479c 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -21,7 +21,7 @@ org.mitre openid-connect-parent - 1.3.4-SNAPSHOT + 1.3.4 openid-connect-server-webapp war diff --git a/openid-connect-server/pom.xml b/openid-connect-server/pom.xml index 84fe62198e..98aebae3ad 100644 --- a/openid-connect-server/pom.xml +++ b/openid-connect-server/pom.xml @@ -23,7 +23,7 @@ org.mitre openid-connect-parent - 1.3.4-SNAPSHOT + 1.3.4 .. diff --git a/pom.xml b/pom.xml index 6824f13603..0dcf6805ae 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.mitre openid-connect-parent - 1.3.4-SNAPSHOT + 1.3.4 MITREid Connect pom diff --git a/uma-server-webapp/pom.xml b/uma-server-webapp/pom.xml index e0485e03a5..5e8de8840c 100644 --- a/uma-server-webapp/pom.xml +++ b/uma-server-webapp/pom.xml @@ -19,7 +19,7 @@ org.mitre openid-connect-parent - 1.3.4-SNAPSHOT + 1.3.4 .. uma-server-webapp diff --git a/uma-server/pom.xml b/uma-server/pom.xml index 2373d34c1c..d063dcc4a3 100644 --- a/uma-server/pom.xml +++ b/uma-server/pom.xml @@ -19,7 +19,7 @@ org.mitre openid-connect-parent - 1.3.4-SNAPSHOT + 1.3.4 .. uma-server From f5df7621533a79a9a738a6301444a50753bcfb22 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Mon, 20 Dec 2021 13:09:11 -0500 Subject: [PATCH 265/269] [maven-release-plugin] prepare for next development iteration --- openid-connect-client/pom.xml | 2 +- openid-connect-common/pom.xml | 2 +- openid-connect-server-webapp/pom.xml | 2 +- openid-connect-server/pom.xml | 2 +- pom.xml | 2 +- uma-server-webapp/pom.xml | 2 +- uma-server/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml index e2f9399cb5..3fbbd9e5ec 100644 --- a/openid-connect-client/pom.xml +++ b/openid-connect-client/pom.xml @@ -22,7 +22,7 @@ openid-connect-parent org.mitre - 1.3.4 + 1.3.5-SNAPSHOT .. openid-connect-client diff --git a/openid-connect-common/pom.xml b/openid-connect-common/pom.xml index cab527332f..08a1111498 100644 --- a/openid-connect-common/pom.xml +++ b/openid-connect-common/pom.xml @@ -22,7 +22,7 @@ openid-connect-parent org.mitre - 1.3.4 + 1.3.5-SNAPSHOT .. openid-connect-common diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index bf2a37479c..a294b1d8bb 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -21,7 +21,7 @@ org.mitre openid-connect-parent - 1.3.4 + 1.3.5-SNAPSHOT openid-connect-server-webapp war diff --git a/openid-connect-server/pom.xml b/openid-connect-server/pom.xml index 98aebae3ad..eff1246112 100644 --- a/openid-connect-server/pom.xml +++ b/openid-connect-server/pom.xml @@ -23,7 +23,7 @@ org.mitre openid-connect-parent - 1.3.4 + 1.3.5-SNAPSHOT .. diff --git a/pom.xml b/pom.xml index 0dcf6805ae..d85b71930e 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.mitre openid-connect-parent - 1.3.4 + 1.3.5-SNAPSHOT MITREid Connect pom diff --git a/uma-server-webapp/pom.xml b/uma-server-webapp/pom.xml index 5e8de8840c..a1db8a2739 100644 --- a/uma-server-webapp/pom.xml +++ b/uma-server-webapp/pom.xml @@ -19,7 +19,7 @@ org.mitre openid-connect-parent - 1.3.4 + 1.3.5-SNAPSHOT .. uma-server-webapp diff --git a/uma-server/pom.xml b/uma-server/pom.xml index d063dcc4a3..a35038b57e 100644 --- a/uma-server/pom.xml +++ b/uma-server/pom.xml @@ -19,7 +19,7 @@ org.mitre openid-connect-parent - 1.3.4 + 1.3.5-SNAPSHOT .. uma-server From 8a58d1260020eeb5121eb4f2097209441919ebfa Mon Sep 17 00:00:00 2001 From: faidh Date: Fri, 25 Mar 2022 11:11:55 +0200 Subject: [PATCH 266/269] Merge latest MitreID with updated 3pps back to GitHub fork --- openid-connect-client/pom.xml | 2 +- openid-connect-common/pom.xml | 2 +- openid-connect-server-webapp/pom.xml | 2 +- .../src/main/webapp/WEB-INF/tags/header.tag | 1 + .../main/webapp/resources/js/lib/backbone.js | 3955 +-- .../main/webapp/resources/js/lib/jquery.js | 17218 +++++------ .../resources/js/lib/moment-with-locales.js | 24858 +++++++++++----- .../webapp/resources/js/lib/underscore.js | 3128 +- openid-connect-server/pom.xml | 2 +- pom.xml | 2 +- uma-server-webapp/pom.xml | 2 +- uma-server/pom.xml | 4 +- 12 files changed, 29385 insertions(+), 19791 deletions(-) diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml index 3fbbd9e5ec..0767d3f271 100644 --- a/openid-connect-client/pom.xml +++ b/openid-connect-client/pom.xml @@ -22,7 +22,7 @@ openid-connect-parent org.mitre - 1.3.5-SNAPSHOT + 1.3.5 .. openid-connect-client diff --git a/openid-connect-common/pom.xml b/openid-connect-common/pom.xml index 08a1111498..e091d67364 100644 --- a/openid-connect-common/pom.xml +++ b/openid-connect-common/pom.xml @@ -22,7 +22,7 @@ openid-connect-parent org.mitre - 1.3.5-SNAPSHOT + 1.3.5 .. openid-connect-common diff --git a/openid-connect-server-webapp/pom.xml b/openid-connect-server-webapp/pom.xml index a294b1d8bb..a529cbd18b 100644 --- a/openid-connect-server-webapp/pom.xml +++ b/openid-connect-server-webapp/pom.xml @@ -21,7 +21,7 @@ org.mitre openid-connect-parent - 1.3.5-SNAPSHOT + 1.3.5 openid-connect-server-webapp war diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/header.tag b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/header.tag index f4b4430cf9..94f68be330 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/header.tag +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/tags/header.tag @@ -14,6 +14,7 @@ + diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/lib/backbone.js b/openid-connect-server-webapp/src/main/webapp/resources/js/lib/backbone.js index 58800425c7..3cdcfdc821 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/lib/backbone.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/lib/backbone.js @@ -1,1873 +1,2096 @@ -// Backbone.js 1.2.1 +// Backbone.js 1.4.0 -// (c) 2010-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// (c) 2010-2019 Jeremy Ashkenas and DocumentCloud // Backbone may be freely distributed under the MIT license. // For all details and documentation: // http://backbonejs.org (function(factory) { - // Establish the root object, `window` (`self`) in the browser, or `global` on the server. - // We use `self` instead of `window` for `WebWorker` support. - var root = (typeof self == 'object' && self.self == self && self) || - (typeof global == 'object' && global.global == global && global); - - // Set up Backbone appropriately for the environment. Start with AMD. - if (typeof define === 'function' && define.amd) { - define(['underscore', 'jquery', 'exports'], function(_, $, exports) { - // Export global even in AMD case in case this script is loaded with - // others that may still expect a global Backbone. - root.Backbone = factory(root, exports, _, $); - }); - - // Next for Node.js or CommonJS. jQuery may not be needed as a module. - } else if (typeof exports !== 'undefined') { - var _ = require('underscore'), $; - try { $ = require('jquery'); } catch(e) {} - factory(root, exports, _, $); - - // Finally, as a browser global. - } else { - root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$)); - } - -}(function(root, Backbone, _, $) { - - // Initial Setup - // ------------- - - // Save the previous value of the `Backbone` variable, so that it can be - // restored later on, if `noConflict` is used. - var previousBackbone = root.Backbone; - - // Create a local reference to a common array method we'll want to use later. - var slice = [].slice; - - // Current version of the library. Keep in sync with `package.json`. - Backbone.VERSION = '1.2.1'; - - // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns - // the `$` variable. - Backbone.$ = $; - - // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable - // to its previous owner. Returns a reference to this Backbone object. - Backbone.noConflict = function() { - root.Backbone = previousBackbone; - return this; - }; - - // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option - // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and - // set a `X-Http-Method-Override` header. - Backbone.emulateHTTP = false; - - // Turn on `emulateJSON` to support legacy servers that can't deal with direct - // `application/json` requests ... this will encode the body as - // `application/x-www-form-urlencoded` instead and will send the model in a - // form param named `model`. - Backbone.emulateJSON = false; - - // Proxy Underscore methods to a Backbone class' prototype using a - // particular attribute as the data argument - var addMethod = function(length, method, attribute) { - switch (length) { - case 1: return function() { - return _[method](this[attribute]); - }; - case 2: return function(value) { - return _[method](this[attribute], value); - }; - case 3: return function(iteratee, context) { - return _[method](this[attribute], iteratee, context); - }; - case 4: return function(iteratee, defaultVal, context) { - return _[method](this[attribute], iteratee, defaultVal, context); - }; - default: return function() { - var args = slice.call(arguments); - args.unshift(this[attribute]); - return _[method].apply(_, args); - }; - } - }; - var addUnderscoreMethods = function(Class, methods, attribute) { - _.each(methods, function(length, method) { - if (_[method]) Class.prototype[method] = addMethod(length, method, attribute); - }); - }; - - // Backbone.Events - // --------------- - - // A module that can be mixed in to *any object* in order to provide it with - // custom events. You may bind with `on` or remove with `off` callback - // functions to an event; `trigger`-ing an event fires all callbacks in - // succession. - // - // var object = {}; - // _.extend(object, Backbone.Events); - // object.on('expand', function(){ alert('expanded'); }); - // object.trigger('expand'); - // - var Events = Backbone.Events = {}; - - // Regular expression used to split event strings. - var eventSplitter = /\s+/; - - // Iterates over the standard `event, callback` (as well as the fancy multiple - // space-separated events `"change blur", callback` and jQuery-style event - // maps `{event: callback}`), reducing them by manipulating `memo`. - // Passes a normalized single event name and callback, as well as any - // optional `opts`. - var eventsApi = function(iteratee, memo, name, callback, opts) { - var i = 0, names; - if (name && typeof name === 'object') { - // Handle event maps. - if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback; - for (names = _.keys(name); i < names.length ; i++) { - memo = iteratee(memo, names[i], name[names[i]], opts); - } - } else if (name && eventSplitter.test(name)) { - // Handle space separated event names. - for (names = name.split(eventSplitter); i < names.length; i++) { - memo = iteratee(memo, names[i], callback, opts); - } - } else { - memo = iteratee(memo, name, callback, opts); - } - return memo; - }; - - // Bind an event to a `callback` function. Passing `"all"` will bind - // the callback to all events fired. - Events.on = function(name, callback, context) { - return internalOn(this, name, callback, context); - }; - - // An internal use `on` function, used to guard the `listening` argument from - // the public API. - var internalOn = function(obj, name, callback, context, listening) { - obj._events = eventsApi(onApi, obj._events || {}, name, callback, { - context: context, - ctx: obj, - listening: listening - }); - - if (listening) { - var listeners = obj._listeners || (obj._listeners = {}); - listeners[listening.id] = listening; - } - - return obj; - }; - - // Inversion-of-control versions of `on`. Tell *this* object to listen to - // an event in another object... keeping track of what it's listening to. - Events.listenTo = function(obj, name, callback) { - if (!obj) return this; - var id = obj._listenId || (obj._listenId = _.uniqueId('l')); - var listeningTo = this._listeningTo || (this._listeningTo = {}); - var listening = listeningTo[id]; - - // This object is not listening to any other events on `obj` yet. - // Setup the necessary references to track the listening callbacks. - if (!listening) { - var thisId = this._listenId || (this._listenId = _.uniqueId('l')); - listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0}; - } - - // Bind callbacks on obj, and keep track of them on listening. - internalOn(obj, name, callback, this, listening); - return this; - }; - - // The reducing API that adds a callback to the `events` object. - var onApi = function(events, name, callback, options) { - if (callback) { - var handlers = events[name] || (events[name] = []); - var context = options.context, ctx = options.ctx, listening = options.listening; - if (listening) listening.count++; - - handlers.push({ callback: callback, context: context, ctx: context || ctx, listening: listening }); - } - return events; - }; - - // Remove one or many callbacks. If `context` is null, removes all - // callbacks with that function. If `callback` is null, removes all - // callbacks for the event. If `name` is null, removes all bound - // callbacks for all events. - Events.off = function(name, callback, context) { - if (!this._events) return this; - this._events = eventsApi(offApi, this._events, name, callback, { - context: context, - listeners: this._listeners - }); - return this; - }; - - // Tell this object to stop listening to either specific events ... or - // to every object it's currently listening to. - Events.stopListening = function(obj, name, callback) { - var listeningTo = this._listeningTo; - if (!listeningTo) return this; - - var ids = obj ? [obj._listenId] : _.keys(listeningTo); - - for (var i = 0; i < ids.length; i++) { - var listening = listeningTo[ids[i]]; - - // If listening doesn't exist, this object is not currently - // listening to obj. Break out early. - if (!listening) break; - - listening.obj.off(name, callback, this); - } - if (_.isEmpty(listeningTo)) this._listeningTo = void 0; - - return this; - }; - - // The reducing API that removes a callback from the `events` object. - var offApi = function(events, name, callback, options) { - // No events to consider. - if (!events) return; - - var i = 0, listening; - var context = options.context, listeners = options.listeners; - - // Delete all events listeners and "drop" events. - if (!name && !callback && !context) { - var ids = _.keys(listeners); - for (; i < ids.length; i++) { - listening = listeners[ids[i]]; - delete listeners[listening.id]; - delete listening.listeningTo[listening.objId]; - } - return; - } - - var names = name ? [name] : _.keys(events); - for (; i < names.length; i++) { - name = names[i]; - var handlers = events[name]; - - // Bail out if there are no events stored. - if (!handlers) break; - - // Replace events if there are any remaining. Otherwise, clean up. - var remaining = []; - for (var j = 0; j < handlers.length; j++) { - var handler = handlers[j]; - if ( - callback && callback !== handler.callback && - callback !== handler.callback._callback || - context && context !== handler.context - ) { - remaining.push(handler); - } else { - listening = handler.listening; - if (listening && --listening.count === 0) { - delete listeners[listening.id]; - delete listening.listeningTo[listening.objId]; - } - } - } - - // Update tail event if the list has any events. Otherwise, clean up. - if (remaining.length) { - events[name] = remaining; - } else { - delete events[name]; - } - } - if (_.size(events)) return events; - }; - - // Bind an event to only be triggered a single time. After the first time - // the callback is invoked, it will be removed. When multiple events are - // passed in using the space-separated syntax, the event will fire once for every - // event you passed in, not once for a combination of all events - Events.once = function(name, callback, context) { - // Map the event into a `{event: once}` object. - var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this)); - return this.on(events, void 0, context); - }; - - // Inversion-of-control versions of `once`. - Events.listenToOnce = function(obj, name, callback) { - // Map the event into a `{event: once}` object. - var events = eventsApi(onceMap, {}, name, callback, _.bind(this.stopListening, this, obj)); - return this.listenTo(obj, events); - }; - - // Reduces the event callbacks into a map of `{event: onceWrapper}`. - // `offer` unbinds the `onceWrapper` after it has been called. - var onceMap = function(map, name, callback, offer) { - if (callback) { - var once = map[name] = _.once(function() { - offer(name, once); - callback.apply(this, arguments); - }); - once._callback = callback; - } - return map; - }; - - // Trigger one or many events, firing all bound callbacks. Callbacks are - // passed the same arguments as `trigger` is, apart from the event name - // (unless you're listening on `"all"`, which will cause your callback to - // receive the true name of the event as the first argument). - Events.trigger = function(name) { - if (!this._events) return this; - - var length = Math.max(0, arguments.length - 1); - var args = Array(length); - for (var i = 0; i < length; i++) args[i] = arguments[i + 1]; - - eventsApi(triggerApi, this._events, name, void 0, args); - return this; - }; - - // Handles triggering the appropriate event callbacks. - var triggerApi = function(objEvents, name, cb, args) { - if (objEvents) { - var events = objEvents[name]; - var allEvents = objEvents.all; - if (events && allEvents) allEvents = allEvents.slice(); - if (events) triggerEvents(events, args); - if (allEvents) triggerEvents(allEvents, [name].concat(args)); - } - return objEvents; - }; - - // A difficult-to-believe, but optimized internal dispatch function for - // triggering events. Tries to keep the usual cases speedy (most internal - // Backbone events have 3 arguments). - var triggerEvents = function(events, args) { - var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; - switch (args.length) { - case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; - case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; - case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; - case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; - default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; - } - }; - - // Aliases for backwards compatibility. - Events.bind = Events.on; - Events.unbind = Events.off; - - // Allow the `Backbone` object to serve as a global event bus, for folks who - // want global "pubsub" in a convenient place. - _.extend(Backbone, Events); - - // Backbone.Model - // -------------- - - // Backbone **Models** are the basic data object in the framework -- - // frequently representing a row in a table in a database on your server. - // A discrete chunk of data and a bunch of useful, related methods for - // performing computations and transformations on that data. - - // Create a new model with the specified attributes. A client id (`cid`) - // is automatically generated and assigned for you. - var Model = Backbone.Model = function(attributes, options) { - var attrs = attributes || {}; - options || (options = {}); - this.cid = _.uniqueId(this.cidPrefix); - this.attributes = {}; - if (options.collection) this.collection = options.collection; - if (options.parse) attrs = this.parse(attrs, options) || {}; - attrs = _.defaults({}, attrs, _.result(this, 'defaults')); - this.set(attrs, options); - this.changed = {}; - this.initialize.apply(this, arguments); - }; - - // Attach all inheritable methods to the Model prototype. - _.extend(Model.prototype, Events, { - - // A hash of attributes whose current and previous value differ. - changed: null, - - // The value returned during the last failed validation. - validationError: null, - - // The default name for the JSON `id` attribute is `"id"`. MongoDB and - // CouchDB users may want to set this to `"_id"`. - idAttribute: 'id', - - // The prefix is used to create the client id which is used to identify models locally. - // You may want to override this if you're experiencing name clashes with model ids. - cidPrefix: 'c', - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // Return a copy of the model's `attributes` object. - toJSON: function(options) { - return _.clone(this.attributes); - }, - - // Proxy `Backbone.sync` by default -- but override this if you need - // custom syncing semantics for *this* particular model. - sync: function() { - return Backbone.sync.apply(this, arguments); - }, - - // Get the value of an attribute. - get: function(attr) { - return this.attributes[attr]; - }, - - // Get the HTML-escaped value of an attribute. - escape: function(attr) { - return _.escape(this.get(attr)); - }, - - // Returns `true` if the attribute contains a value that is not null - // or undefined. - has: function(attr) { - return this.get(attr) != null; - }, - - // Special-cased proxy to underscore's `_.matches` method. - matches: function(attrs) { - return !!_.iteratee(attrs, this)(this.attributes); - }, - - // Set a hash of model attributes on the object, firing `"change"`. This is - // the core primitive operation of a model, updating the data and notifying - // anyone who needs to know about the change in state. The heart of the beast. - set: function(key, val, options) { - if (key == null) return this; - - // Handle both `"key", value` and `{key: value}` -style arguments. - var attrs; - if (typeof key === 'object') { - attrs = key; - options = val; - } else { - (attrs = {})[key] = val; - } - - options || (options = {}); - - // Run validation. - if (!this._validate(attrs, options)) return false; - - // Extract attributes and options. - var unset = options.unset; - var silent = options.silent; - var changes = []; - var changing = this._changing; - this._changing = true; - - if (!changing) { - this._previousAttributes = _.clone(this.attributes); - this.changed = {}; - } - - var current = this.attributes; - var changed = this.changed; - var prev = this._previousAttributes; - - // Check for changes of `id`. - if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; - - // For each `set` attribute, update or delete the current value. - for (var attr in attrs) { - val = attrs[attr]; - if (!_.isEqual(current[attr], val)) changes.push(attr); - if (!_.isEqual(prev[attr], val)) { - changed[attr] = val; - } else { - delete changed[attr]; - } - unset ? delete current[attr] : current[attr] = val; - } - - // Trigger all relevant attribute changes. - if (!silent) { - if (changes.length) this._pending = options; - for (var i = 0; i < changes.length; i++) { - this.trigger('change:' + changes[i], this, current[changes[i]], options); - } - } - - // You might be wondering why there's a `while` loop here. Changes can - // be recursively nested within `"change"` events. - if (changing) return this; - if (!silent) { - while (this._pending) { - options = this._pending; - this._pending = false; - this.trigger('change', this, options); - } - } - this._pending = false; - this._changing = false; - return this; - }, - - // Remove an attribute from the model, firing `"change"`. `unset` is a noop - // if the attribute doesn't exist. - unset: function(attr, options) { - return this.set(attr, void 0, _.extend({}, options, {unset: true})); - }, - - // Clear all attributes on the model, firing `"change"`. - clear: function(options) { - var attrs = {}; - for (var key in this.attributes) attrs[key] = void 0; - return this.set(attrs, _.extend({}, options, {unset: true})); - }, - - // Determine if the model has changed since the last `"change"` event. - // If you specify an attribute name, determine if that attribute has changed. - hasChanged: function(attr) { - if (attr == null) return !_.isEmpty(this.changed); - return _.has(this.changed, attr); - }, - - // Return an object containing all the attributes that have changed, or - // false if there are no changed attributes. Useful for determining what - // parts of a view need to be updated and/or what attributes need to be - // persisted to the server. Unset attributes will be set to undefined. - // You can also pass an attributes object to diff against the model, - // determining if there *would be* a change. - changedAttributes: function(diff) { - if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; - var old = this._changing ? this._previousAttributes : this.attributes; - var changed = {}; - for (var attr in diff) { - var val = diff[attr]; - if (_.isEqual(old[attr], val)) continue; - changed[attr] = val; - } - return _.size(changed) ? changed : false; - }, - - // Get the previous value of an attribute, recorded at the time the last - // `"change"` event was fired. - previous: function(attr) { - if (attr == null || !this._previousAttributes) return null; - return this._previousAttributes[attr]; - }, - - // Get all of the attributes of the model at the time of the previous - // `"change"` event. - previousAttributes: function() { - return _.clone(this._previousAttributes); - }, - - // Fetch the model from the server, merging the response with the model's - // local attributes. Any changed attributes will trigger a "change" event. - fetch: function(options) { - options = _.extend({parse: true}, options); - var model = this; - var success = options.success; - options.success = function(resp) { - var serverAttrs = options.parse ? model.parse(resp, options) : resp; - if (!model.set(serverAttrs, options)) return false; - if (success) success.call(options.context, model, resp, options); - model.trigger('sync', model, resp, options); - }; - wrapError(this, options); - return this.sync('read', this, options); - }, - - // Set a hash of model attributes, and sync the model to the server. - // If the server returns an attributes hash that differs, the model's - // state will be `set` again. - save: function(key, val, options) { - // Handle both `"key", value` and `{key: value}` -style arguments. - var attrs; - if (key == null || typeof key === 'object') { - attrs = key; - options = val; - } else { - (attrs = {})[key] = val; - } - - options = _.extend({validate: true, parse: true}, options); - var wait = options.wait; - - // If we're not waiting and attributes exist, save acts as - // `set(attr).save(null, opts)` with validation. Otherwise, check if - // the model will be valid when the attributes, if any, are set. - if (attrs && !wait) { - if (!this.set(attrs, options)) return false; - } else { - if (!this._validate(attrs, options)) return false; - } - - // After a successful server-side save, the client is (optionally) - // updated with the server-side state. - var model = this; - var success = options.success; - var attributes = this.attributes; - options.success = function(resp) { - // Ensure attributes are restored during synchronous saves. - model.attributes = attributes; - var serverAttrs = options.parse ? model.parse(resp, options) : resp; - if (wait) serverAttrs = _.extend({}, attrs, serverAttrs); - if (serverAttrs && !model.set(serverAttrs, options)) return false; - if (success) success.call(options.context, model, resp, options); - model.trigger('sync', model, resp, options); - }; - wrapError(this, options); - - // Set temporary attributes if `{wait: true}` to properly find new ids. - if (attrs && wait) this.attributes = _.extend({}, attributes, attrs); - - var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); - if (method === 'patch' && !options.attrs) options.attrs = attrs; - var xhr = this.sync(method, this, options); - - // Restore attributes. - this.attributes = attributes; - - return xhr; - }, - - // Destroy this model on the server if it was already persisted. - // Optimistically removes the model from its collection, if it has one. - // If `wait: true` is passed, waits for the server to respond before removal. - destroy: function(options) { - options = options ? _.clone(options) : {}; - var model = this; - var success = options.success; - var wait = options.wait; - - var destroy = function() { - model.stopListening(); - model.trigger('destroy', model, model.collection, options); - }; - - options.success = function(resp) { - if (wait) destroy(); - if (success) success.call(options.context, model, resp, options); - if (!model.isNew()) model.trigger('sync', model, resp, options); - }; - - var xhr = false; - if (this.isNew()) { - _.defer(options.success); - } else { - wrapError(this, options); - xhr = this.sync('delete', this, options); - } - if (!wait) destroy(); - return xhr; - }, - - // Default URL for the model's representation on the server -- if you're - // using Backbone's restful methods, override this to change the endpoint - // that will be called. - url: function() { - var base = - _.result(this, 'urlRoot') || - _.result(this.collection, 'url') || - urlError(); - if (this.isNew()) return base; - var id = this.get(this.idAttribute); - return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id); - }, - - // **parse** converts a response into the hash of attributes to be `set` on - // the model. The default implementation is just to pass the response along. - parse: function(resp, options) { - return resp; - }, - - // Create a new model with identical attributes to this one. - clone: function() { - return new this.constructor(this.attributes); - }, - - // A model is new if it has never been saved to the server, and lacks an id. - isNew: function() { - return !this.has(this.idAttribute); - }, - - // Check if the model is currently in a valid state. - isValid: function(options) { - return this._validate({}, _.defaults({validate: true}, options)); - }, - - // Run validation against the next complete set of model attributes, - // returning `true` if all is well. Otherwise, fire an `"invalid"` event. - _validate: function(attrs, options) { - if (!options.validate || !this.validate) return true; - attrs = _.extend({}, this.attributes, attrs); - var error = this.validationError = this.validate(attrs, options) || null; - if (!error) return true; - this.trigger('invalid', this, error, _.extend(options, {validationError: error})); - return false; - } - - }); - - // Underscore methods that we want to implement on the Model. - var modelMethods = { keys: 1, values: 1, pairs: 1, invert: 1, pick: 0, - omit: 0, chain: 1, isEmpty: 1 }; - - // Mix in each Underscore method as a proxy to `Model#attributes`. - addUnderscoreMethods(Model, modelMethods, 'attributes'); - - // Backbone.Collection - // ------------------- - - // If models tend to represent a single row of data, a Backbone Collection is - // more analogous to a table full of data ... or a small slice or page of that - // table, or a collection of rows that belong together for a particular reason - // -- all of the messages in this particular folder, all of the documents - // belonging to this particular author, and so on. Collections maintain - // indexes of their models, both in order, and for lookup by `id`. - - // Create a new **Collection**, perhaps to contain a specific type of `model`. - // If a `comparator` is specified, the Collection will maintain - // its models in sort order, as they're added and removed. - var Collection = Backbone.Collection = function(models, options) { - options || (options = {}); - if (options.model) this.model = options.model; - if (options.comparator !== void 0) this.comparator = options.comparator; - this._reset(); - this.initialize.apply(this, arguments); - if (models) this.reset(models, _.extend({silent: true}, options)); - }; - - // Default options for `Collection#set`. - var setOptions = {add: true, remove: true, merge: true}; - var addOptions = {add: true, remove: false}; - - // Define the Collection's inheritable methods. - _.extend(Collection.prototype, Events, { - - // The default model for a collection is just a **Backbone.Model**. - // This should be overridden in most cases. - model: Model, - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // The JSON representation of a Collection is an array of the - // models' attributes. - toJSON: function(options) { - return this.map(function(model) { return model.toJSON(options); }); - }, - - // Proxy `Backbone.sync` by default. - sync: function() { - return Backbone.sync.apply(this, arguments); - }, - - // Add a model, or list of models to the set. - add: function(models, options) { - return this.set(models, _.extend({merge: false}, options, addOptions)); - }, - - // Remove a model, or a list of models from the set. - remove: function(models, options) { - options = _.extend({}, options); - var singular = !_.isArray(models); - models = singular ? [models] : _.clone(models); - var removed = this._removeModels(models, options); - if (!options.silent && removed) this.trigger('update', this, options); - return singular ? removed[0] : removed; - }, - - // Update a collection by `set`-ing a new list of models, adding new ones, - // removing models that are no longer present, and merging models that - // already exist in the collection, as necessary. Similar to **Model#set**, - // the core operation for updating the data contained by the collection. - set: function(models, options) { - options = _.defaults({}, options, setOptions); - if (options.parse && !this._isModel(models)) models = this.parse(models, options); - var singular = !_.isArray(models); - models = singular ? (models ? [models] : []) : models.slice(); - var id, model, attrs, existing, sort; - var at = options.at; - if (at != null) at = +at; - if (at < 0) at += this.length + 1; - var sortable = this.comparator && (at == null) && options.sort !== false; - var sortAttr = _.isString(this.comparator) ? this.comparator : null; - var toAdd = [], toRemove = [], modelMap = {}; - var add = options.add, merge = options.merge, remove = options.remove; - var order = !sortable && add && remove ? [] : false; - var orderChanged = false; - - // Turn bare objects into model references, and prevent invalid models - // from being added. - for (var i = 0; i < models.length; i++) { - attrs = models[i]; - - // If a duplicate is found, prevent it from being added and - // optionally merge it into the existing model. - if (existing = this.get(attrs)) { - if (remove) modelMap[existing.cid] = true; - if (merge && attrs !== existing) { - attrs = this._isModel(attrs) ? attrs.attributes : attrs; - if (options.parse) attrs = existing.parse(attrs, options); - existing.set(attrs, options); - if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true; - } - models[i] = existing; - - // If this is a new, valid model, push it to the `toAdd` list. - } else if (add) { - model = models[i] = this._prepareModel(attrs, options); - if (!model) continue; - toAdd.push(model); - this._addReference(model, options); - } - - // Do not add multiple models with the same `id`. - model = existing || model; - if (!model) continue; - id = this.modelId(model.attributes); - if (order && (model.isNew() || !modelMap[id])) { - order.push(model); - - // Check to see if this is actually a new model at this index. - orderChanged = orderChanged || !this.models[i] || model.cid !== this.models[i].cid; - } - - modelMap[id] = true; - } - - // Remove nonexistent models if appropriate. - if (remove) { - for (var i = 0; i < this.length; i++) { - if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model); - } - if (toRemove.length) this._removeModels(toRemove, options); - } - - // See if sorting is needed, update `length` and splice in new models. - if (toAdd.length || orderChanged) { - if (sortable) sort = true; - this.length += toAdd.length; - if (at != null) { - for (var i = 0; i < toAdd.length; i++) { - this.models.splice(at + i, 0, toAdd[i]); - } - } else { - if (order) this.models.length = 0; - var orderedModels = order || toAdd; - for (var i = 0; i < orderedModels.length; i++) { - this.models.push(orderedModels[i]); - } - } - } - - // Silently sort the collection if appropriate. - if (sort) this.sort({silent: true}); - - // Unless silenced, it's time to fire all appropriate add/sort events. - if (!options.silent) { - var addOpts = at != null ? _.clone(options) : options; - for (var i = 0; i < toAdd.length; i++) { - if (at != null) addOpts.index = at + i; - (model = toAdd[i]).trigger('add', model, this, addOpts); - } - if (sort || orderChanged) this.trigger('sort', this, options); - if (toAdd.length || toRemove.length) this.trigger('update', this, options); - } - - // Return the added (or merged) model (or models). - return singular ? models[0] : models; - }, - - // When you have more items than you want to add or remove individually, - // you can reset the entire set with a new list of models, without firing - // any granular `add` or `remove` events. Fires `reset` when finished. - // Useful for bulk operations and optimizations. - reset: function(models, options) { - options = options ? _.clone(options) : {}; - for (var i = 0; i < this.models.length; i++) { - this._removeReference(this.models[i], options); - } - options.previousModels = this.models; - this._reset(); - models = this.add(models, _.extend({silent: true}, options)); - if (!options.silent) this.trigger('reset', this, options); - return models; - }, - - // Add a model to the end of the collection. - push: function(model, options) { - return this.add(model, _.extend({at: this.length}, options)); - }, - - // Remove a model from the end of the collection. - pop: function(options) { - var model = this.at(this.length - 1); - return this.remove(model, options); - }, - - // Add a model to the beginning of the collection. - unshift: function(model, options) { - return this.add(model, _.extend({at: 0}, options)); - }, - - // Remove a model from the beginning of the collection. - shift: function(options) { - var model = this.at(0); - return this.remove(model, options); - }, - - // Slice out a sub-array of models from the collection. - slice: function() { - return slice.apply(this.models, arguments); - }, - - // Get a model from the set by id. - get: function(obj) { - if (obj == null) return void 0; - var id = this.modelId(this._isModel(obj) ? obj.attributes : obj); - return this._byId[obj] || this._byId[id] || this._byId[obj.cid]; - }, - - // Get the model at the given index. - at: function(index) { - if (index < 0) index += this.length; - return this.models[index]; - }, - - // Return models with matching attributes. Useful for simple cases of - // `filter`. - where: function(attrs, first) { - var matches = _.matches(attrs); - return this[first ? 'find' : 'filter'](function(model) { - return matches(model.attributes); - }); - }, - - // Return the first model with matching attributes. Useful for simple cases - // of `find`. - findWhere: function(attrs) { - return this.where(attrs, true); - }, - - // Force the collection to re-sort itself. You don't need to call this under - // normal circumstances, as the set will maintain sort order as each item - // is added. - sort: function(options) { - if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); - options || (options = {}); - - // Run sort based on type of `comparator`. - if (_.isString(this.comparator) || this.comparator.length === 1) { - this.models = this.sortBy(this.comparator, this); - } else { - this.models.sort(_.bind(this.comparator, this)); - } - - if (!options.silent) this.trigger('sort', this, options); - return this; - }, - - // Pluck an attribute from each model in the collection. - pluck: function(attr) { - return _.invoke(this.models, 'get', attr); - }, - - // Fetch the default set of models for this collection, resetting the - // collection when they arrive. If `reset: true` is passed, the response - // data will be passed through the `reset` method instead of `set`. - fetch: function(options) { - options = _.extend({parse: true}, options); - var success = options.success; - var collection = this; - options.success = function(resp) { - var method = options.reset ? 'reset' : 'set'; - collection[method](resp, options); - if (success) success.call(options.context, collection, resp, options); - collection.trigger('sync', collection, resp, options); - }; - wrapError(this, options); - return this.sync('read', this, options); - }, - - // Create a new instance of a model in this collection. Add the model to the - // collection immediately, unless `wait: true` is passed, in which case we - // wait for the server to agree. - create: function(model, options) { - options = options ? _.clone(options) : {}; - var wait = options.wait; - model = this._prepareModel(model, options); - if (!model) return false; - if (!wait) this.add(model, options); - var collection = this; - var success = options.success; - options.success = function(model, resp, callbackOpts) { - if (wait) collection.add(model, callbackOpts); - if (success) success.call(callbackOpts.context, model, resp, callbackOpts); - }; - model.save(null, options); - return model; - }, - - // **parse** converts a response into a list of models to be added to the - // collection. The default implementation is just to pass it through. - parse: function(resp, options) { - return resp; - }, - - // Create a new collection with an identical list of models as this one. - clone: function() { - return new this.constructor(this.models, { - model: this.model, - comparator: this.comparator - }); - }, - - // Define how to uniquely identify models in the collection. - modelId: function (attrs) { - return attrs[this.model.prototype.idAttribute || 'id']; - }, - - // Private method to reset all internal state. Called when the collection - // is first initialized or reset. - _reset: function() { - this.length = 0; - this.models = []; - this._byId = {}; - }, - - // Prepare a hash of attributes (or other model) to be added to this - // collection. - _prepareModel: function(attrs, options) { - if (this._isModel(attrs)) { - if (!attrs.collection) attrs.collection = this; - return attrs; - } - options = options ? _.clone(options) : {}; - options.collection = this; - var model = new this.model(attrs, options); - if (!model.validationError) return model; - this.trigger('invalid', this, model.validationError, options); - return false; - }, - - // Internal method called by both remove and set. - // Returns removed models, or false if nothing is removed. - _removeModels: function(models, options) { - var removed = []; - for (var i = 0; i < models.length; i++) { - var model = this.get(models[i]); - if (!model) continue; - - var index = this.indexOf(model); - this.models.splice(index, 1); - this.length--; - - if (!options.silent) { - options.index = index; - model.trigger('remove', model, this, options); - } - - removed.push(model); - this._removeReference(model, options); - } - return removed.length ? removed : false; - }, - - // Method for checking whether an object should be considered a model for - // the purposes of adding to the collection. - _isModel: function (model) { - return model instanceof Model; - }, - - // Internal method to create a model's ties to a collection. - _addReference: function(model, options) { - this._byId[model.cid] = model; - var id = this.modelId(model.attributes); - if (id != null) this._byId[id] = model; - model.on('all', this._onModelEvent, this); - }, - - // Internal method to sever a model's ties to a collection. - _removeReference: function(model, options) { - delete this._byId[model.cid]; - var id = this.modelId(model.attributes); - if (id != null) delete this._byId[id]; - if (this === model.collection) delete model.collection; - model.off('all', this._onModelEvent, this); - }, - - // Internal method called every time a model in the set fires an event. - // Sets need to update their indexes when models change ids. All other - // events simply proxy through. "add" and "remove" events that originate - // in other collections are ignored. - _onModelEvent: function(event, model, collection, options) { - if ((event === 'add' || event === 'remove') && collection !== this) return; - if (event === 'destroy') this.remove(model, options); - if (event === 'change') { - var prevId = this.modelId(model.previousAttributes()); - var id = this.modelId(model.attributes); - if (prevId !== id) { - if (prevId != null) delete this._byId[prevId]; - if (id != null) this._byId[id] = model; - } - } - this.trigger.apply(this, arguments); - } - - }); - - // Underscore methods that we want to implement on the Collection. - // 90% of the core usefulness of Backbone Collections is actually implemented - // right here: - var collectionMethods = { forEach: 3, each: 3, map: 3, collect: 3, reduce: 4, - foldl: 4, inject: 4, reduceRight: 4, foldr: 4, find: 3, detect: 3, filter: 3, - select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 2, - contains: 2, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3, - head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3, - without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3, - isEmpty: 1, chain: 1, sample: 3, partition: 3 }; - - // Mix in each Underscore method as a proxy to `Collection#models`. - addUnderscoreMethods(Collection, collectionMethods, 'models'); - - // Underscore methods that take a property name as an argument. - var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy']; - - // Use attributes instead of properties. - _.each(attributeMethods, function(method) { - if (!_[method]) return; - Collection.prototype[method] = function(value, context) { - var iterator = _.isFunction(value) ? value : function(model) { - return model.get(value); - }; - return _[method](this.models, iterator, context); - }; - }); - - // Backbone.View - // ------------- - - // Backbone Views are almost more convention than they are actual code. A View - // is simply a JavaScript object that represents a logical chunk of UI in the - // DOM. This might be a single item, an entire list, a sidebar or panel, or - // even the surrounding frame which wraps your whole app. Defining a chunk of - // UI as a **View** allows you to define your DOM events declaratively, without - // having to worry about render order ... and makes it easy for the view to - // react to specific changes in the state of your models. - - // Creating a Backbone.View creates its initial element outside of the DOM, - // if an existing element is not provided... - var View = Backbone.View = function(options) { - this.cid = _.uniqueId('view'); - _.extend(this, _.pick(options, viewOptions)); - this._ensureElement(); - this.initialize.apply(this, arguments); - }; - - // Cached regex to split keys for `delegate`. - var delegateEventSplitter = /^(\S+)\s*(.*)$/; - - // List of view options to be merged as properties. - var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; - - // Set up all inheritable **Backbone.View** properties and methods. - _.extend(View.prototype, Events, { - - // The default `tagName` of a View's element is `"div"`. - tagName: 'div', - - // jQuery delegate for element lookup, scoped to DOM elements within the - // current view. This should be preferred to global lookups where possible. - $: function(selector) { - return this.$el.find(selector); - }, - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // **render** is the core function that your view should override, in order - // to populate its element (`this.el`), with the appropriate HTML. The - // convention is for **render** to always return `this`. - render: function() { - return this; - }, - - // Remove this view by taking the element out of the DOM, and removing any - // applicable Backbone.Events listeners. - remove: function() { - this._removeElement(); - this.stopListening(); - return this; - }, - - // Remove this view's element from the document and all event listeners - // attached to it. Exposed for subclasses using an alternative DOM - // manipulation API. - _removeElement: function() { - this.$el.remove(); - }, - - // Change the view's element (`this.el` property) and re-delegate the - // view's events on the new element. - setElement: function(element) { - this.undelegateEvents(); - this._setElement(element); - this.delegateEvents(); - return this; - }, - - // Creates the `this.el` and `this.$el` references for this view using the - // given `el`. `el` can be a CSS selector or an HTML string, a jQuery - // context or an element. Subclasses can override this to utilize an - // alternative DOM manipulation API and are only required to set the - // `this.el` property. - _setElement: function(el) { - this.$el = el instanceof Backbone.$ ? el : Backbone.$(el); - this.el = this.$el[0]; - }, - - // Set callbacks, where `this.events` is a hash of - // - // *{"event selector": "callback"}* - // - // { - // 'mousedown .title': 'edit', - // 'click .button': 'save', - // 'click .open': function(e) { ... } - // } - // - // pairs. Callbacks will be bound to the view, with `this` set properly. - // Uses event delegation for efficiency. - // Omitting the selector binds the event to `this.el`. - delegateEvents: function(events) { - events || (events = _.result(this, 'events')); - if (!events) return this; - this.undelegateEvents(); - for (var key in events) { - var method = events[key]; - if (!_.isFunction(method)) method = this[method]; - if (!method) continue; - var match = key.match(delegateEventSplitter); - this.delegate(match[1], match[2], _.bind(method, this)); - } - return this; - }, - - // Add a single event listener to the view's element (or a child element - // using `selector`). This only works for delegate-able events: not `focus`, - // `blur`, and not `change`, `submit`, and `reset` in Internet Explorer. - delegate: function(eventName, selector, listener) { - this.$el.on(eventName + '.delegateEvents' + this.cid, selector, listener); - return this; - }, - - // Clears all callbacks previously bound to the view by `delegateEvents`. - // You usually don't need to use this, but may wish to if you have multiple - // Backbone views attached to the same DOM element. - undelegateEvents: function() { - if (this.$el) this.$el.off('.delegateEvents' + this.cid); - return this; - }, - - // A finer-grained `undelegateEvents` for removing a single delegated event. - // `selector` and `listener` are both optional. - undelegate: function(eventName, selector, listener) { - this.$el.off(eventName + '.delegateEvents' + this.cid, selector, listener); - return this; - }, - - // Produces a DOM element to be assigned to your view. Exposed for - // subclasses using an alternative DOM manipulation API. - _createElement: function(tagName) { - return document.createElement(tagName); - }, - - // Ensure that the View has a DOM element to render into. - // If `this.el` is a string, pass it through `$()`, take the first - // matching element, and re-assign it to `el`. Otherwise, create - // an element from the `id`, `className` and `tagName` properties. - _ensureElement: function() { - if (!this.el) { - var attrs = _.extend({}, _.result(this, 'attributes')); - if (this.id) attrs.id = _.result(this, 'id'); - if (this.className) attrs['class'] = _.result(this, 'className'); - this.setElement(this._createElement(_.result(this, 'tagName'))); - this._setAttributes(attrs); - } else { - this.setElement(_.result(this, 'el')); - } - }, - - // Set attributes from a hash on this view's element. Exposed for - // subclasses using an alternative DOM manipulation API. - _setAttributes: function(attributes) { - this.$el.attr(attributes); - } - - }); - - // Backbone.sync - // ------------- - - // Override this function to change the manner in which Backbone persists - // models to the server. You will be passed the type of request, and the - // model in question. By default, makes a RESTful Ajax request - // to the model's `url()`. Some possible customizations could be: - // - // * Use `setTimeout` to batch rapid-fire updates into a single request. - // * Send up the models as XML instead of JSON. - // * Persist models via WebSockets instead of Ajax. - // - // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests - // as `POST`, with a `_method` parameter containing the true HTTP method, - // as well as all requests with the body as `application/x-www-form-urlencoded` - // instead of `application/json` with the model in a param named `model`. - // Useful when interfacing with server-side languages like **PHP** that make - // it difficult to read the body of `PUT` requests. - Backbone.sync = function(method, model, options) { - var type = methodMap[method]; - - // Default options, unless specified. - _.defaults(options || (options = {}), { - emulateHTTP: Backbone.emulateHTTP, - emulateJSON: Backbone.emulateJSON - }); - - // Default JSON-request options. - var params = {type: type, dataType: 'json'}; - - // Ensure that we have a URL. - if (!options.url) { - params.url = _.result(model, 'url') || urlError(); - } - - // Ensure that we have the appropriate request data. - if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { - params.contentType = 'application/json'; - params.data = JSON.stringify(options.attrs || model.toJSON(options)); - } - - // For older servers, emulate JSON by encoding the request into an HTML-form. - if (options.emulateJSON) { - params.contentType = 'application/x-www-form-urlencoded'; - params.data = params.data ? {model: params.data} : {}; - } - - // For older servers, emulate HTTP by mimicking the HTTP method with `_method` - // And an `X-HTTP-Method-Override` header. - if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) { - params.type = 'POST'; - if (options.emulateJSON) params.data._method = type; - var beforeSend = options.beforeSend; - options.beforeSend = function(xhr) { - xhr.setRequestHeader('X-HTTP-Method-Override', type); - if (beforeSend) return beforeSend.apply(this, arguments); - }; - } - - // Don't process data on a non-GET request. - if (params.type !== 'GET' && !options.emulateJSON) { - params.processData = false; - } - - // Pass along `textStatus` and `errorThrown` from jQuery. - var error = options.error; - options.error = function(xhr, textStatus, errorThrown) { - options.textStatus = textStatus; - options.errorThrown = errorThrown; - if (error) error.call(options.context, xhr, textStatus, errorThrown); - }; - - // Make the request, allowing the user to override any Ajax options. - var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); - model.trigger('request', model, xhr, options); - return xhr; - }; - - // Map from CRUD to HTTP for our default `Backbone.sync` implementation. - var methodMap = { - 'create': 'POST', - 'update': 'PUT', - 'patch': 'PATCH', - 'delete': 'DELETE', - 'read': 'GET' - }; - - // Set the default implementation of `Backbone.ajax` to proxy through to `$`. - // Override this if you'd like to use a different library. - Backbone.ajax = function() { - return Backbone.$.ajax.apply(Backbone.$, arguments); - }; - - // Backbone.Router - // --------------- - - // Routers map faux-URLs to actions, and fire events when routes are - // matched. Creating a new one sets its `routes` hash, if not set statically. - var Router = Backbone.Router = function(options) { - options || (options = {}); - if (options.routes) this.routes = options.routes; - this._bindRoutes(); - this.initialize.apply(this, arguments); - }; - - // Cached regular expressions for matching named param parts and splatted - // parts of route strings. - var optionalParam = /\((.*?)\)/g; - var namedParam = /(\(\?)?:\w+/g; - var splatParam = /\*\w+/g; - var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; - - // Set up all inheritable **Backbone.Router** properties and methods. - _.extend(Router.prototype, Events, { - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // Manually bind a single named route to a callback. For example: - // - // this.route('search/:query/p:num', 'search', function(query, num) { - // ... - // }); - // - route: function(route, name, callback) { - if (!_.isRegExp(route)) route = this._routeToRegExp(route); - if (_.isFunction(name)) { - callback = name; - name = ''; - } - if (!callback) callback = this[name]; - var router = this; - Backbone.history.route(route, function(fragment) { - var args = router._extractParameters(route, fragment); - if (router.execute(callback, args, name) !== false) { - router.trigger.apply(router, ['route:' + name].concat(args)); - router.trigger('route', name, args); - Backbone.history.trigger('route', router, name, args); - } - }); - return this; - }, - - // Execute a route handler with the provided parameters. This is an - // excellent place to do pre-route setup or post-route cleanup. - execute: function(callback, args, name) { - if (callback) callback.apply(this, args); - }, - - // Simple proxy to `Backbone.history` to save a fragment into the history. - navigate: function(fragment, options) { - Backbone.history.navigate(fragment, options); - return this; - }, - - // Bind all defined routes to `Backbone.history`. We have to reverse the - // order of the routes here to support behavior where the most general - // routes can be defined at the bottom of the route map. - _bindRoutes: function() { - if (!this.routes) return; - this.routes = _.result(this, 'routes'); - var route, routes = _.keys(this.routes); - while ((route = routes.pop()) != null) { - this.route(route, this.routes[route]); - } - }, - - // Convert a route string into a regular expression, suitable for matching - // against the current location hash. - _routeToRegExp: function(route) { - route = route.replace(escapeRegExp, '\\$&') - .replace(optionalParam, '(?:$1)?') - .replace(namedParam, function(match, optional) { - return optional ? match : '([^/?]+)'; - }) - .replace(splatParam, '([^?]*?)'); - return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'); - }, - - // Given a route, and a URL fragment that it matches, return the array of - // extracted decoded parameters. Empty or unmatched parameters will be - // treated as `null` to normalize cross-browser behavior. - _extractParameters: function(route, fragment) { - var params = route.exec(fragment).slice(1); - return _.map(params, function(param, i) { - // Don't decode the search params. - if (i === params.length - 1) return param || null; - return param ? decodeURIComponent(param) : null; - }); - } - - }); - - // Backbone.History - // ---------------- - - // Handles cross-browser history management, based on either - // [pushState](http://diveintohtml5.info/history.html) and real URLs, or - // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange) - // and URL fragments. If the browser supports neither (old IE, natch), - // falls back to polling. - var History = Backbone.History = function() { - this.handlers = []; - _.bindAll(this, 'checkUrl'); - - // Ensure that `History` can be used outside of the browser. - if (typeof window !== 'undefined') { - this.location = window.location; - this.history = window.history; - } - }; - - // Cached regex for stripping a leading hash/slash and trailing space. - var routeStripper = /^[#\/]|\s+$/g; - - // Cached regex for stripping leading and trailing slashes. - var rootStripper = /^\/+|\/+$/g; - - // Cached regex for stripping urls of hash. - var pathStripper = /#.*$/; - - // Has the history handling already been started? - History.started = false; - - // Set up all inheritable **Backbone.History** properties and methods. - _.extend(History.prototype, Events, { - - // The default interval to poll for hash changes, if necessary, is - // twenty times a second. - interval: 50, - - // Are we at the app root? - atRoot: function() { - var path = this.location.pathname.replace(/[^\/]$/, '$&/'); - return path === this.root && !this.getSearch(); - }, - - // Does the pathname match the root? - matchRoot: function() { - var path = this.decodeFragment(this.location.pathname); - var root = path.slice(0, this.root.length - 1) + '/'; - return root === this.root; - }, - - // Unicode characters in `location.pathname` are percent encoded so they're - // decoded for comparison. `%25` should not be decoded since it may be part - // of an encoded parameter. - decodeFragment: function(fragment) { - return decodeURI(fragment.replace(/%25/g, '%2525')); - }, - - // In IE6, the hash fragment and search params are incorrect if the - // fragment contains `?`. - getSearch: function() { - var match = this.location.href.replace(/#.*/, '').match(/\?.+/); - return match ? match[0] : ''; - }, - - // Gets the true hash value. Cannot use location.hash directly due to bug - // in Firefox where location.hash will always be decoded. - getHash: function(window) { - var match = (window || this).location.href.match(/#(.*)$/); - return match ? match[1] : ''; - }, - - // Get the pathname and search params, without the root. - getPath: function() { - var path = this.decodeFragment( - this.location.pathname + this.getSearch() - ).slice(this.root.length - 1); - return path.charAt(0) === '/' ? path.slice(1) : path; - }, - - // Get the cross-browser normalized URL fragment from the path or hash. - getFragment: function(fragment) { - if (fragment == null) { - if (this._usePushState || !this._wantsHashChange) { - fragment = this.getPath(); - } else { - fragment = this.getHash(); - } - } - return fragment.replace(routeStripper, ''); - }, - - // Start the hash change handling, returning `true` if the current URL matches - // an existing route, and `false` otherwise. - start: function(options) { - if (History.started) throw new Error('Backbone.history has already been started'); - History.started = true; - - // Figure out the initial configuration. Do we need an iframe? - // Is pushState desired ... is it available? - this.options = _.extend({root: '/'}, this.options, options); - this.root = this.options.root; - this._wantsHashChange = this.options.hashChange !== false; - this._hasHashChange = 'onhashchange' in window; - this._useHashChange = this._wantsHashChange && this._hasHashChange; - this._wantsPushState = !!this.options.pushState; - this._hasPushState = !!(this.history && this.history.pushState); - this._usePushState = this._wantsPushState && this._hasPushState; - this.fragment = this.getFragment(); - - // Normalize root to always include a leading and trailing slash. - this.root = ('/' + this.root + '/').replace(rootStripper, '/'); - - // Transition from hashChange to pushState or vice versa if both are - // requested. - if (this._wantsHashChange && this._wantsPushState) { - - // If we've started off with a route from a `pushState`-enabled - // browser, but we're currently in a browser that doesn't support it... - if (!this._hasPushState && !this.atRoot()) { - var root = this.root.slice(0, -1) || '/'; - this.location.replace(root + '#' + this.getPath()); - // Return immediately as browser will do redirect to new url - return true; - - // Or if we've started out with a hash-based route, but we're currently - // in a browser where it could be `pushState`-based instead... - } else if (this._hasPushState && this.atRoot()) { - this.navigate(this.getHash(), {replace: true}); - } - - } - - // Proxy an iframe to handle location events if the browser doesn't - // support the `hashchange` event, HTML5 history, or the user wants - // `hashChange` but not `pushState`. - if (!this._hasHashChange && this._wantsHashChange && !this._usePushState) { - this.iframe = document.createElement('iframe'); - this.iframe.src = 'javascript:0'; - this.iframe.style.display = 'none'; - this.iframe.tabIndex = -1; - var body = document.body; - // Using `appendChild` will throw on IE < 9 if the document is not ready. - var iWindow = body.insertBefore(this.iframe, body.firstChild).contentWindow; - iWindow.document.open(); - iWindow.document.close(); - iWindow.location.hash = '#' + this.fragment; - } - - // Add a cross-platform `addEventListener` shim for older browsers. - var addEventListener = window.addEventListener || function (eventName, listener) { - return attachEvent('on' + eventName, listener); - }; - - // Depending on whether we're using pushState or hashes, and whether - // 'onhashchange' is supported, determine how we check the URL state. - if (this._usePushState) { - addEventListener('popstate', this.checkUrl, false); - } else if (this._useHashChange && !this.iframe) { - addEventListener('hashchange', this.checkUrl, false); - } else if (this._wantsHashChange) { - this._checkUrlInterval = setInterval(this.checkUrl, this.interval); - } - - if (!this.options.silent) return this.loadUrl(); - }, - - // Disable Backbone.history, perhaps temporarily. Not useful in a real app, - // but possibly useful for unit testing Routers. - stop: function() { - // Add a cross-platform `removeEventListener` shim for older browsers. - var removeEventListener = window.removeEventListener || function (eventName, listener) { - return detachEvent('on' + eventName, listener); - }; - - // Remove window listeners. - if (this._usePushState) { - removeEventListener('popstate', this.checkUrl, false); - } else if (this._useHashChange && !this.iframe) { - removeEventListener('hashchange', this.checkUrl, false); - } - - // Clean up the iframe if necessary. - if (this.iframe) { - document.body.removeChild(this.iframe); - this.iframe = null; - } - - // Some environments will throw when clearing an undefined interval. - if (this._checkUrlInterval) clearInterval(this._checkUrlInterval); - History.started = false; - }, - - // Add a route to be tested when the fragment changes. Routes added later - // may override previous routes. - route: function(route, callback) { - this.handlers.unshift({route: route, callback: callback}); - }, - - // Checks the current URL to see if it has changed, and if it has, - // calls `loadUrl`, normalizing across the hidden iframe. - checkUrl: function(e) { - var current = this.getFragment(); - - // If the user pressed the back button, the iframe's hash will have - // changed and we should use that for comparison. - if (current === this.fragment && this.iframe) { - current = this.getHash(this.iframe.contentWindow); - } - - if (current === this.fragment) return false; - if (this.iframe) this.navigate(current); - this.loadUrl(); - }, - - // Attempt to load the current URL fragment. If a route succeeds with a - // match, returns `true`. If no defined routes matches the fragment, - // returns `false`. - loadUrl: function(fragment) { - // If the root doesn't match, no routes can match either. - if (!this.matchRoot()) return false; - fragment = this.fragment = this.getFragment(fragment); - return _.any(this.handlers, function(handler) { - if (handler.route.test(fragment)) { - handler.callback(fragment); - return true; - } - }); - }, - - // Save a fragment into the hash history, or replace the URL state if the - // 'replace' option is passed. You are responsible for properly URL-encoding - // the fragment in advance. - // - // The options object can contain `trigger: true` if you wish to have the - // route callback be fired (not usually desirable), or `replace: true`, if - // you wish to modify the current URL without adding an entry to the history. - navigate: function(fragment, options) { - if (!History.started) return false; - if (!options || options === true) options = {trigger: !!options}; - - // Normalize the fragment. - fragment = this.getFragment(fragment || ''); - - // Don't include a trailing slash on the root. - var root = this.root; - if (fragment === '' || fragment.charAt(0) === '?') { - root = root.slice(0, -1) || '/'; - } - var url = root + fragment; - - // Strip the hash and decode for matching. - fragment = this.decodeFragment(fragment.replace(pathStripper, '')); - - if (this.fragment === fragment) return; - this.fragment = fragment; - - // If pushState is available, we use it to set the fragment as a real URL. - if (this._usePushState) { - this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url); - - // If hash changes haven't been explicitly disabled, update the hash - // fragment to store history. - } else if (this._wantsHashChange) { - this._updateHash(this.location, fragment, options.replace); - if (this.iframe && (fragment !== this.getHash(this.iframe.contentWindow))) { - var iWindow = this.iframe.contentWindow; - - // Opening and closing the iframe tricks IE7 and earlier to push a - // history entry on hash-tag change. When replace is true, we don't - // want this. - if (!options.replace) { - iWindow.document.open(); - iWindow.document.close(); - } - - this._updateHash(iWindow.location, fragment, options.replace); - } - - // If you've told us that you explicitly don't want fallback hashchange- - // based history, then `navigate` becomes a page refresh. - } else { - return this.location.assign(url); - } - if (options.trigger) return this.loadUrl(fragment); - }, - - // Update the hash location, either replacing the current entry, or adding - // a new one to the browser history. - _updateHash: function(location, fragment, replace) { - if (replace) { - var href = location.href.replace(/(javascript:|#).*$/, ''); - location.replace(href + '#' + fragment); - } else { - // Some browsers require that `hash` contains a leading #. - location.hash = '#' + fragment; - } - } - - }); - - // Create the default Backbone.history. - Backbone.history = new History; - - // Helpers - // ------- - - // Helper function to correctly set up the prototype chain for subclasses. - // Similar to `goog.inherits`, but uses a hash of prototype properties and - // class properties to be extended. - var extend = function(protoProps, staticProps) { - var parent = this; - var child; - - // The constructor function for the new subclass is either defined by you - // (the "constructor" property in your `extend` definition), or defaulted - // by us to simply call the parent constructor. - if (protoProps && _.has(protoProps, 'constructor')) { - child = protoProps.constructor; - } else { - child = function(){ return parent.apply(this, arguments); }; - } - - // Add static properties to the constructor function, if supplied. - _.extend(child, parent, staticProps); - - // Set the prototype chain to inherit from `parent`, without calling - // `parent` constructor function. - var Surrogate = function(){ this.constructor = child; }; - Surrogate.prototype = parent.prototype; - child.prototype = new Surrogate; - - // Add prototype properties (instance properties) to the subclass, - // if supplied. - if (protoProps) _.extend(child.prototype, protoProps); - - // Set a convenience property in case the parent's prototype is needed - // later. - child.__super__ = parent.prototype; - - return child; - }; - - // Set up inheritance for the model, collection, router, view and history. - Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend; - - // Throw an error when a URL is needed, and none is supplied. - var urlError = function() { - throw new Error('A "url" property or function must be specified'); - }; - - // Wrap an optional error callback with a fallback error event. - var wrapError = function(model, options) { - var error = options.error; - options.error = function(resp) { - if (error) error.call(options.context, model, resp, options); - model.trigger('error', model, resp, options); - }; - }; - - return Backbone; - -})); + // Establish the root object, `window` (`self`) in the browser, or `global` on the server. + // We use `self` instead of `window` for `WebWorker` support. + var root = typeof self == 'object' && self.self === self && self || + typeof global == 'object' && global.global === global && global; + + // Set up Backbone appropriately for the environment. Start with AMD. + if (typeof define === 'function' && define.amd) { + define(['underscore', 'jquery', 'exports'], function(_, $, exports) { + // Export global even in AMD case in case this script is loaded with + // others that may still expect a global Backbone. + root.Backbone = factory(root, exports, _, $); + }); + + // Next for Node.js or CommonJS. jQuery may not be needed as a module. + } else if (typeof exports !== 'undefined') { + var _ = require('underscore'), $; + try { $ = require('jquery'); } catch (e) {} + factory(root, exports, _, $); + + // Finally, as a browser global. + } else { + root.Backbone = factory(root, {}, root._, root.jQuery || root.Zepto || root.ender || root.$); + } + +})(function(root, Backbone, _, $) { + + // Initial Setup + // ------------- + + // Save the previous value of the `Backbone` variable, so that it can be + // restored later on, if `noConflict` is used. + var previousBackbone = root.Backbone; + + // Create a local reference to a common array method we'll want to use later. + var slice = Array.prototype.slice; + + // Current version of the library. Keep in sync with `package.json`. + Backbone.VERSION = '1.4.0'; + + // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns + // the `$` variable. + Backbone.$ = $; + + // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable + // to its previous owner. Returns a reference to this Backbone object. + Backbone.noConflict = function() { + root.Backbone = previousBackbone; + return this; + }; + + // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option + // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and + // set a `X-Http-Method-Override` header. + Backbone.emulateHTTP = false; + + // Turn on `emulateJSON` to support legacy servers that can't deal with direct + // `application/json` requests ... this will encode the body as + // `application/x-www-form-urlencoded` instead and will send the model in a + // form param named `model`. + Backbone.emulateJSON = false; + + // Backbone.Events + // --------------- + + // A module that can be mixed in to *any object* in order to provide it with + // a custom event channel. You may bind a callback to an event with `on` or + // remove with `off`; `trigger`-ing an event fires all callbacks in + // succession. + // + // var object = {}; + // _.extend(object, Backbone.Events); + // object.on('expand', function(){ alert('expanded'); }); + // object.trigger('expand'); + // + var Events = Backbone.Events = {}; + + // Regular expression used to split event strings. + var eventSplitter = /\s+/; + + // A private global variable to share between listeners and listenees. + var _listening; + + // Iterates over the standard `event, callback` (as well as the fancy multiple + // space-separated events `"change blur", callback` and jQuery-style event + // maps `{event: callback}`). + var eventsApi = function(iteratee, events, name, callback, opts) { + var i = 0, names; + if (name && typeof name === 'object') { + // Handle event maps. + if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback; + for (names = _.keys(name); i < names.length ; i++) { + events = eventsApi(iteratee, events, names[i], name[names[i]], opts); + } + } else if (name && eventSplitter.test(name)) { + // Handle space-separated event names by delegating them individually. + for (names = name.split(eventSplitter); i < names.length; i++) { + events = iteratee(events, names[i], callback, opts); + } + } else { + // Finally, standard events. + events = iteratee(events, name, callback, opts); + } + return events; + }; + + // Bind an event to a `callback` function. Passing `"all"` will bind + // the callback to all events fired. + Events.on = function(name, callback, context) { + this._events = eventsApi(onApi, this._events || {}, name, callback, { + context: context, + ctx: this, + listening: _listening + }); + + if (_listening) { + var listeners = this._listeners || (this._listeners = {}); + listeners[_listening.id] = _listening; + // Allow the listening to use a counter, instead of tracking + // callbacks for library interop + _listening.interop = false; + } + + return this; + }; + + // Inversion-of-control versions of `on`. Tell *this* object to listen to + // an event in another object... keeping track of what it's listening to + // for easier unbinding later. + Events.listenTo = function(obj, name, callback) { + if (!obj) return this; + var id = obj._listenId || (obj._listenId = _.uniqueId('l')); + var listeningTo = this._listeningTo || (this._listeningTo = {}); + var listening = _listening = listeningTo[id]; + + // This object is not listening to any other events on `obj` yet. + // Setup the necessary references to track the listening callbacks. + if (!listening) { + this._listenId || (this._listenId = _.uniqueId('l')); + listening = _listening = listeningTo[id] = new Listening(this, obj); + } + + // Bind callbacks on obj. + var error = tryCatchOn(obj, name, callback, this); + _listening = void 0; + + if (error) throw error; + // If the target obj is not Backbone.Events, track events manually. + if (listening.interop) listening.on(name, callback); + + return this; + }; + + // The reducing API that adds a callback to the `events` object. + var onApi = function(events, name, callback, options) { + if (callback) { + var handlers = events[name] || (events[name] = []); + var context = options.context, ctx = options.ctx, listening = options.listening; + if (listening) listening.count++; + + handlers.push({callback: callback, context: context, ctx: context || ctx, listening: listening}); + } + return events; + }; + + // An try-catch guarded #on function, to prevent poisoning the global + // `_listening` variable. + var tryCatchOn = function(obj, name, callback, context) { + try { + obj.on(name, callback, context); + } catch (e) { + return e; + } + }; + + // Remove one or many callbacks. If `context` is null, removes all + // callbacks with that function. If `callback` is null, removes all + // callbacks for the event. If `name` is null, removes all bound + // callbacks for all events. + Events.off = function(name, callback, context) { + if (!this._events) return this; + this._events = eventsApi(offApi, this._events, name, callback, { + context: context, + listeners: this._listeners + }); + + return this; + }; + + // Tell this object to stop listening to either specific events ... or + // to every object it's currently listening to. + Events.stopListening = function(obj, name, callback) { + var listeningTo = this._listeningTo; + if (!listeningTo) return this; + + var ids = obj ? [obj._listenId] : _.keys(listeningTo); + for (var i = 0; i < ids.length; i++) { + var listening = listeningTo[ids[i]]; + + // If listening doesn't exist, this object is not currently + // listening to obj. Break out early. + if (!listening) break; + + listening.obj.off(name, callback, this); + if (listening.interop) listening.off(name, callback); + } + if (_.isEmpty(listeningTo)) this._listeningTo = void 0; + + return this; + }; + + // The reducing API that removes a callback from the `events` object. + var offApi = function(events, name, callback, options) { + if (!events) return; + + var context = options.context, listeners = options.listeners; + var i = 0, names; + + // Delete all event listeners and "drop" events. + if (!name && !context && !callback) { + for (names = _.keys(listeners); i < names.length; i++) { + listeners[names[i]].cleanup(); + } + return; + } + + names = name ? [name] : _.keys(events); + for (; i < names.length; i++) { + name = names[i]; + var handlers = events[name]; + + // Bail out if there are no events stored. + if (!handlers) break; + + // Find any remaining events. + var remaining = []; + for (var j = 0; j < handlers.length; j++) { + var handler = handlers[j]; + if ( + callback && callback !== handler.callback && + callback !== handler.callback._callback || + context && context !== handler.context + ) { + remaining.push(handler); + } else { + var listening = handler.listening; + if (listening) listening.off(name, callback); + } + } + + // Replace events if there are any remaining. Otherwise, clean up. + if (remaining.length) { + events[name] = remaining; + } else { + delete events[name]; + } + } + + return events; + }; + + // Bind an event to only be triggered a single time. After the first time + // the callback is invoked, its listener will be removed. If multiple events + // are passed in using the space-separated syntax, the handler will fire + // once for each event, not once for a combination of all events. + Events.once = function(name, callback, context) { + // Map the event into a `{event: once}` object. + var events = eventsApi(onceMap, {}, name, callback, this.off.bind(this)); + if (typeof name === 'string' && context == null) callback = void 0; + return this.on(events, callback, context); + }; + + // Inversion-of-control versions of `once`. + Events.listenToOnce = function(obj, name, callback) { + // Map the event into a `{event: once}` object. + var events = eventsApi(onceMap, {}, name, callback, this.stopListening.bind(this, obj)); + return this.listenTo(obj, events); + }; + + // Reduces the event callbacks into a map of `{event: onceWrapper}`. + // `offer` unbinds the `onceWrapper` after it has been called. + var onceMap = function(map, name, callback, offer) { + if (callback) { + var once = map[name] = _.once(function() { + offer(name, once); + callback.apply(this, arguments); + }); + once._callback = callback; + } + return map; + }; + + // Trigger one or many events, firing all bound callbacks. Callbacks are + // passed the same arguments as `trigger` is, apart from the event name + // (unless you're listening on `"all"`, which will cause your callback to + // receive the true name of the event as the first argument). + Events.trigger = function(name) { + if (!this._events) return this; + + var length = Math.max(0, arguments.length - 1); + var args = Array(length); + for (var i = 0; i < length; i++) args[i] = arguments[i + 1]; + + eventsApi(triggerApi, this._events, name, void 0, args); + return this; + }; + + // Handles triggering the appropriate event callbacks. + var triggerApi = function(objEvents, name, callback, args) { + if (objEvents) { + var events = objEvents[name]; + var allEvents = objEvents.all; + if (events && allEvents) allEvents = allEvents.slice(); + if (events) triggerEvents(events, args); + if (allEvents) triggerEvents(allEvents, [name].concat(args)); + } + return objEvents; + }; + + // A difficult-to-believe, but optimized internal dispatch function for + // triggering events. Tries to keep the usual cases speedy (most internal + // Backbone events have 3 arguments). + var triggerEvents = function(events, args) { + var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; + switch (args.length) { + case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; + case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; + case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; + case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; + default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; + } + }; + + // A listening class that tracks and cleans up memory bindings + // when all callbacks have been offed. + var Listening = function(listener, obj) { + this.id = listener._listenId; + this.listener = listener; + this.obj = obj; + this.interop = true; + this.count = 0; + this._events = void 0; + }; + + Listening.prototype.on = Events.on; + + // Offs a callback (or several). + // Uses an optimized counter if the listenee uses Backbone.Events. + // Otherwise, falls back to manual tracking to support events + // library interop. + Listening.prototype.off = function(name, callback) { + var cleanup; + if (this.interop) { + this._events = eventsApi(offApi, this._events, name, callback, { + context: void 0, + listeners: void 0 + }); + cleanup = !this._events; + } else { + this.count--; + cleanup = this.count === 0; + } + if (cleanup) this.cleanup(); + }; + + // Cleans up memory bindings between the listener and the listenee. + Listening.prototype.cleanup = function() { + delete this.listener._listeningTo[this.obj._listenId]; + if (!this.interop) delete this.obj._listeners[this.id]; + }; + + // Aliases for backwards compatibility. + Events.bind = Events.on; + Events.unbind = Events.off; + + // Allow the `Backbone` object to serve as a global event bus, for folks who + // want global "pubsub" in a convenient place. + _.extend(Backbone, Events); + + // Backbone.Model + // -------------- + + // Backbone **Models** are the basic data object in the framework -- + // frequently representing a row in a table in a database on your server. + // A discrete chunk of data and a bunch of useful, related methods for + // performing computations and transformations on that data. + + // Create a new model with the specified attributes. A client id (`cid`) + // is automatically generated and assigned for you. + var Model = Backbone.Model = function(attributes, options) { + var attrs = attributes || {}; + options || (options = {}); + this.preinitialize.apply(this, arguments); + this.cid = _.uniqueId(this.cidPrefix); + this.attributes = {}; + if (options.collection) this.collection = options.collection; + if (options.parse) attrs = this.parse(attrs, options) || {}; + var defaults = _.result(this, 'defaults'); + attrs = _.defaults(_.extend({}, defaults, attrs), defaults); + this.set(attrs, options); + this.changed = {}; + this.initialize.apply(this, arguments); + }; + + // Attach all inheritable methods to the Model prototype. + _.extend(Model.prototype, Events, { + + // A hash of attributes whose current and previous value differ. + changed: null, + + // The value returned during the last failed validation. + validationError: null, + + // The default name for the JSON `id` attribute is `"id"`. MongoDB and + // CouchDB users may want to set this to `"_id"`. + idAttribute: 'id', + + // The prefix is used to create the client id which is used to identify models locally. + // You may want to override this if you're experiencing name clashes with model ids. + cidPrefix: 'c', + + // preinitialize is an empty function by default. You can override it with a function + // or object. preinitialize will run before any instantiation logic is run in the Model. + preinitialize: function(){}, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // Return a copy of the model's `attributes` object. + toJSON: function(options) { + return _.clone(this.attributes); + }, + + // Proxy `Backbone.sync` by default -- but override this if you need + // custom syncing semantics for *this* particular model. + sync: function() { + return Backbone.sync.apply(this, arguments); + }, + + // Get the value of an attribute. + get: function(attr) { + return this.attributes[attr]; + }, + + // Get the HTML-escaped value of an attribute. + escape: function(attr) { + return _.escape(this.get(attr)); + }, + + // Returns `true` if the attribute contains a value that is not null + // or undefined. + has: function(attr) { + return this.get(attr) != null; + }, + + // Special-cased proxy to underscore's `_.matches` method. + matches: function(attrs) { + return !!_.iteratee(attrs, this)(this.attributes); + }, + + // Set a hash of model attributes on the object, firing `"change"`. This is + // the core primitive operation of a model, updating the data and notifying + // anyone who needs to know about the change in state. The heart of the beast. + set: function(key, val, options) { + if (key == null) return this; + + // Handle both `"key", value` and `{key: value}` -style arguments. + var attrs; + if (typeof key === 'object') { + attrs = key; + options = val; + } else { + (attrs = {})[key] = val; + } + + options || (options = {}); + + // Run validation. + if (!this._validate(attrs, options)) return false; + + // Extract attributes and options. + var unset = options.unset; + var silent = options.silent; + var changes = []; + var changing = this._changing; + this._changing = true; + + if (!changing) { + this._previousAttributes = _.clone(this.attributes); + this.changed = {}; + } + + var current = this.attributes; + var changed = this.changed; + var prev = this._previousAttributes; + + // For each `set` attribute, update or delete the current value. + for (var attr in attrs) { + val = attrs[attr]; + if (!_.isEqual(current[attr], val)) changes.push(attr); + if (!_.isEqual(prev[attr], val)) { + changed[attr] = val; + } else { + delete changed[attr]; + } + unset ? delete current[attr] : current[attr] = val; + } + + // Update the `id`. + if (this.idAttribute in attrs) this.id = this.get(this.idAttribute); + + // Trigger all relevant attribute changes. + if (!silent) { + if (changes.length) this._pending = options; + for (var i = 0; i < changes.length; i++) { + this.trigger('change:' + changes[i], this, current[changes[i]], options); + } + } + + // You might be wondering why there's a `while` loop here. Changes can + // be recursively nested within `"change"` events. + if (changing) return this; + if (!silent) { + while (this._pending) { + options = this._pending; + this._pending = false; + this.trigger('change', this, options); + } + } + this._pending = false; + this._changing = false; + return this; + }, + + // Remove an attribute from the model, firing `"change"`. `unset` is a noop + // if the attribute doesn't exist. + unset: function(attr, options) { + return this.set(attr, void 0, _.extend({}, options, {unset: true})); + }, + + // Clear all attributes on the model, firing `"change"`. + clear: function(options) { + var attrs = {}; + for (var key in this.attributes) attrs[key] = void 0; + return this.set(attrs, _.extend({}, options, {unset: true})); + }, + + // Determine if the model has changed since the last `"change"` event. + // If you specify an attribute name, determine if that attribute has changed. + hasChanged: function(attr) { + if (attr == null) return !_.isEmpty(this.changed); + return _.has(this.changed, attr); + }, + + // Return an object containing all the attributes that have changed, or + // false if there are no changed attributes. Useful for determining what + // parts of a view need to be updated and/or what attributes need to be + // persisted to the server. Unset attributes will be set to undefined. + // You can also pass an attributes object to diff against the model, + // determining if there *would be* a change. + changedAttributes: function(diff) { + if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; + var old = this._changing ? this._previousAttributes : this.attributes; + var changed = {}; + var hasChanged; + for (var attr in diff) { + var val = diff[attr]; + if (_.isEqual(old[attr], val)) continue; + changed[attr] = val; + hasChanged = true; + } + return hasChanged ? changed : false; + }, + + // Get the previous value of an attribute, recorded at the time the last + // `"change"` event was fired. + previous: function(attr) { + if (attr == null || !this._previousAttributes) return null; + return this._previousAttributes[attr]; + }, + + // Get all of the attributes of the model at the time of the previous + // `"change"` event. + previousAttributes: function() { + return _.clone(this._previousAttributes); + }, + + // Fetch the model from the server, merging the response with the model's + // local attributes. Any changed attributes will trigger a "change" event. + fetch: function(options) { + options = _.extend({parse: true}, options); + var model = this; + var success = options.success; + options.success = function(resp) { + var serverAttrs = options.parse ? model.parse(resp, options) : resp; + if (!model.set(serverAttrs, options)) return false; + if (success) success.call(options.context, model, resp, options); + model.trigger('sync', model, resp, options); + }; + wrapError(this, options); + return this.sync('read', this, options); + }, + + // Set a hash of model attributes, and sync the model to the server. + // If the server returns an attributes hash that differs, the model's + // state will be `set` again. + save: function(key, val, options) { + // Handle both `"key", value` and `{key: value}` -style arguments. + var attrs; + if (key == null || typeof key === 'object') { + attrs = key; + options = val; + } else { + (attrs = {})[key] = val; + } + + options = _.extend({validate: true, parse: true}, options); + var wait = options.wait; + + // If we're not waiting and attributes exist, save acts as + // `set(attr).save(null, opts)` with validation. Otherwise, check if + // the model will be valid when the attributes, if any, are set. + if (attrs && !wait) { + if (!this.set(attrs, options)) return false; + } else if (!this._validate(attrs, options)) { + return false; + } + + // After a successful server-side save, the client is (optionally) + // updated with the server-side state. + var model = this; + var success = options.success; + var attributes = this.attributes; + options.success = function(resp) { + // Ensure attributes are restored during synchronous saves. + model.attributes = attributes; + var serverAttrs = options.parse ? model.parse(resp, options) : resp; + if (wait) serverAttrs = _.extend({}, attrs, serverAttrs); + if (serverAttrs && !model.set(serverAttrs, options)) return false; + if (success) success.call(options.context, model, resp, options); + model.trigger('sync', model, resp, options); + }; + wrapError(this, options); + + // Set temporary attributes if `{wait: true}` to properly find new ids. + if (attrs && wait) this.attributes = _.extend({}, attributes, attrs); + + var method = this.isNew() ? 'create' : options.patch ? 'patch' : 'update'; + if (method === 'patch' && !options.attrs) options.attrs = attrs; + var xhr = this.sync(method, this, options); + + // Restore attributes. + this.attributes = attributes; + + return xhr; + }, + + // Destroy this model on the server if it was already persisted. + // Optimistically removes the model from its collection, if it has one. + // If `wait: true` is passed, waits for the server to respond before removal. + destroy: function(options) { + options = options ? _.clone(options) : {}; + var model = this; + var success = options.success; + var wait = options.wait; + + var destroy = function() { + model.stopListening(); + model.trigger('destroy', model, model.collection, options); + }; + + options.success = function(resp) { + if (wait) destroy(); + if (success) success.call(options.context, model, resp, options); + if (!model.isNew()) model.trigger('sync', model, resp, options); + }; + + var xhr = false; + if (this.isNew()) { + _.defer(options.success); + } else { + wrapError(this, options); + xhr = this.sync('delete', this, options); + } + if (!wait) destroy(); + return xhr; + }, + + // Default URL for the model's representation on the server -- if you're + // using Backbone's restful methods, override this to change the endpoint + // that will be called. + url: function() { + var base = + _.result(this, 'urlRoot') || + _.result(this.collection, 'url') || + urlError(); + if (this.isNew()) return base; + var id = this.get(this.idAttribute); + return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id); + }, + + // **parse** converts a response into the hash of attributes to be `set` on + // the model. The default implementation is just to pass the response along. + parse: function(resp, options) { + return resp; + }, + + // Create a new model with identical attributes to this one. + clone: function() { + return new this.constructor(this.attributes); + }, + + // A model is new if it has never been saved to the server, and lacks an id. + isNew: function() { + return !this.has(this.idAttribute); + }, + + // Check if the model is currently in a valid state. + isValid: function(options) { + return this._validate({}, _.extend({}, options, {validate: true})); + }, + + // Run validation against the next complete set of model attributes, + // returning `true` if all is well. Otherwise, fire an `"invalid"` event. + _validate: function(attrs, options) { + if (!options.validate || !this.validate) return true; + attrs = _.extend({}, this.attributes, attrs); + var error = this.validationError = this.validate(attrs, options) || null; + if (!error) return true; + this.trigger('invalid', this, error, _.extend(options, {validationError: error})); + return false; + } + + }); + + // Backbone.Collection + // ------------------- + + // If models tend to represent a single row of data, a Backbone Collection is + // more analogous to a table full of data ... or a small slice or page of that + // table, or a collection of rows that belong together for a particular reason + // -- all of the messages in this particular folder, all of the documents + // belonging to this particular author, and so on. Collections maintain + // indexes of their models, both in order, and for lookup by `id`. + + // Create a new **Collection**, perhaps to contain a specific type of `model`. + // If a `comparator` is specified, the Collection will maintain + // its models in sort order, as they're added and removed. + var Collection = Backbone.Collection = function(models, options) { + options || (options = {}); + this.preinitialize.apply(this, arguments); + if (options.model) this.model = options.model; + if (options.comparator !== void 0) this.comparator = options.comparator; + this._reset(); + this.initialize.apply(this, arguments); + if (models) this.reset(models, _.extend({silent: true}, options)); + }; + + // Default options for `Collection#set`. + var setOptions = {add: true, remove: true, merge: true}; + var addOptions = {add: true, remove: false}; + + // Splices `insert` into `array` at index `at`. + var splice = function(array, insert, at) { + at = Math.min(Math.max(at, 0), array.length); + var tail = Array(array.length - at); + var length = insert.length; + var i; + for (i = 0; i < tail.length; i++) tail[i] = array[i + at]; + for (i = 0; i < length; i++) array[i + at] = insert[i]; + for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i]; + }; + + // Define the Collection's inheritable methods. + _.extend(Collection.prototype, Events, { + + // The default model for a collection is just a **Backbone.Model**. + // This should be overridden in most cases. + model: Model, + + + // preinitialize is an empty function by default. You can override it with a function + // or object. preinitialize will run before any instantiation logic is run in the Collection. + preinitialize: function(){}, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // The JSON representation of a Collection is an array of the + // models' attributes. + toJSON: function(options) { + return this.map(function(model) { return model.toJSON(options); }); + }, + + // Proxy `Backbone.sync` by default. + sync: function() { + return Backbone.sync.apply(this, arguments); + }, + + // Add a model, or list of models to the set. `models` may be Backbone + // Models or raw JavaScript objects to be converted to Models, or any + // combination of the two. + add: function(models, options) { + return this.set(models, _.extend({merge: false}, options, addOptions)); + }, + + // Remove a model, or a list of models from the set. + remove: function(models, options) { + options = _.extend({}, options); + var singular = !_.isArray(models); + models = singular ? [models] : models.slice(); + var removed = this._removeModels(models, options); + if (!options.silent && removed.length) { + options.changes = {added: [], merged: [], removed: removed}; + this.trigger('update', this, options); + } + return singular ? removed[0] : removed; + }, + + // Update a collection by `set`-ing a new list of models, adding new ones, + // removing models that are no longer present, and merging models that + // already exist in the collection, as necessary. Similar to **Model#set**, + // the core operation for updating the data contained by the collection. + set: function(models, options) { + if (models == null) return; + + options = _.extend({}, setOptions, options); + if (options.parse && !this._isModel(models)) { + models = this.parse(models, options) || []; + } + + var singular = !_.isArray(models); + models = singular ? [models] : models.slice(); + + var at = options.at; + if (at != null) at = +at; + if (at > this.length) at = this.length; + if (at < 0) at += this.length + 1; + + var set = []; + var toAdd = []; + var toMerge = []; + var toRemove = []; + var modelMap = {}; + + var add = options.add; + var merge = options.merge; + var remove = options.remove; + + var sort = false; + var sortable = this.comparator && at == null && options.sort !== false; + var sortAttr = _.isString(this.comparator) ? this.comparator : null; + + // Turn bare objects into model references, and prevent invalid models + // from being added. + var model, i; + for (i = 0; i < models.length; i++) { + model = models[i]; + + // If a duplicate is found, prevent it from being added and + // optionally merge it into the existing model. + var existing = this.get(model); + if (existing) { + if (merge && model !== existing) { + var attrs = this._isModel(model) ? model.attributes : model; + if (options.parse) attrs = existing.parse(attrs, options); + existing.set(attrs, options); + toMerge.push(existing); + if (sortable && !sort) sort = existing.hasChanged(sortAttr); + } + if (!modelMap[existing.cid]) { + modelMap[existing.cid] = true; + set.push(existing); + } + models[i] = existing; + + // If this is a new, valid model, push it to the `toAdd` list. + } else if (add) { + model = models[i] = this._prepareModel(model, options); + if (model) { + toAdd.push(model); + this._addReference(model, options); + modelMap[model.cid] = true; + set.push(model); + } + } + } + + // Remove stale models. + if (remove) { + for (i = 0; i < this.length; i++) { + model = this.models[i]; + if (!modelMap[model.cid]) toRemove.push(model); + } + if (toRemove.length) this._removeModels(toRemove, options); + } + + // See if sorting is needed, update `length` and splice in new models. + var orderChanged = false; + var replace = !sortable && add && remove; + if (set.length && replace) { + orderChanged = this.length !== set.length || _.some(this.models, function(m, index) { + return m !== set[index]; + }); + this.models.length = 0; + splice(this.models, set, 0); + this.length = this.models.length; + } else if (toAdd.length) { + if (sortable) sort = true; + splice(this.models, toAdd, at == null ? this.length : at); + this.length = this.models.length; + } + + // Silently sort the collection if appropriate. + if (sort) this.sort({silent: true}); + + // Unless silenced, it's time to fire all appropriate add/sort/update events. + if (!options.silent) { + for (i = 0; i < toAdd.length; i++) { + if (at != null) options.index = at + i; + model = toAdd[i]; + model.trigger('add', model, this, options); + } + if (sort || orderChanged) this.trigger('sort', this, options); + if (toAdd.length || toRemove.length || toMerge.length) { + options.changes = { + added: toAdd, + removed: toRemove, + merged: toMerge + }; + this.trigger('update', this, options); + } + } + + // Return the added (or merged) model (or models). + return singular ? models[0] : models; + }, + + // When you have more items than you want to add or remove individually, + // you can reset the entire set with a new list of models, without firing + // any granular `add` or `remove` events. Fires `reset` when finished. + // Useful for bulk operations and optimizations. + reset: function(models, options) { + options = options ? _.clone(options) : {}; + for (var i = 0; i < this.models.length; i++) { + this._removeReference(this.models[i], options); + } + options.previousModels = this.models; + this._reset(); + models = this.add(models, _.extend({silent: true}, options)); + if (!options.silent) this.trigger('reset', this, options); + return models; + }, + + // Add a model to the end of the collection. + push: function(model, options) { + return this.add(model, _.extend({at: this.length}, options)); + }, + + // Remove a model from the end of the collection. + pop: function(options) { + var model = this.at(this.length - 1); + return this.remove(model, options); + }, + + // Add a model to the beginning of the collection. + unshift: function(model, options) { + return this.add(model, _.extend({at: 0}, options)); + }, + + // Remove a model from the beginning of the collection. + shift: function(options) { + var model = this.at(0); + return this.remove(model, options); + }, + + // Slice out a sub-array of models from the collection. + slice: function() { + return slice.apply(this.models, arguments); + }, + + // Get a model from the set by id, cid, model object with id or cid + // properties, or an attributes object that is transformed through modelId. + get: function(obj) { + if (obj == null) return void 0; + return this._byId[obj] || + this._byId[this.modelId(this._isModel(obj) ? obj.attributes : obj)] || + obj.cid && this._byId[obj.cid]; + }, + + // Returns `true` if the model is in the collection. + has: function(obj) { + return this.get(obj) != null; + }, + + // Get the model at the given index. + at: function(index) { + if (index < 0) index += this.length; + return this.models[index]; + }, + + // Return models with matching attributes. Useful for simple cases of + // `filter`. + where: function(attrs, first) { + return this[first ? 'find' : 'filter'](attrs); + }, + + // Return the first model with matching attributes. Useful for simple cases + // of `find`. + findWhere: function(attrs) { + return this.where(attrs, true); + }, + + // Force the collection to re-sort itself. You don't need to call this under + // normal circumstances, as the set will maintain sort order as each item + // is added. + sort: function(options) { + var comparator = this.comparator; + if (!comparator) throw new Error('Cannot sort a set without a comparator'); + options || (options = {}); + + var length = comparator.length; + if (_.isFunction(comparator)) comparator = comparator.bind(this); + + // Run sort based on type of `comparator`. + if (length === 1 || _.isString(comparator)) { + this.models = this.sortBy(comparator); + } else { + this.models.sort(comparator); + } + if (!options.silent) this.trigger('sort', this, options); + return this; + }, + + // Pluck an attribute from each model in the collection. + pluck: function(attr) { + return this.map(attr + ''); + }, + + // Fetch the default set of models for this collection, resetting the + // collection when they arrive. If `reset: true` is passed, the response + // data will be passed through the `reset` method instead of `set`. + fetch: function(options) { + options = _.extend({parse: true}, options); + var success = options.success; + var collection = this; + options.success = function(resp) { + var method = options.reset ? 'reset' : 'set'; + collection[method](resp, options); + if (success) success.call(options.context, collection, resp, options); + collection.trigger('sync', collection, resp, options); + }; + wrapError(this, options); + return this.sync('read', this, options); + }, + + // Create a new instance of a model in this collection. Add the model to the + // collection immediately, unless `wait: true` is passed, in which case we + // wait for the server to agree. + create: function(model, options) { + options = options ? _.clone(options) : {}; + var wait = options.wait; + model = this._prepareModel(model, options); + if (!model) return false; + if (!wait) this.add(model, options); + var collection = this; + var success = options.success; + options.success = function(m, resp, callbackOpts) { + if (wait) collection.add(m, callbackOpts); + if (success) success.call(callbackOpts.context, m, resp, callbackOpts); + }; + model.save(null, options); + return model; + }, + + // **parse** converts a response into a list of models to be added to the + // collection. The default implementation is just to pass it through. + parse: function(resp, options) { + return resp; + }, + + // Create a new collection with an identical list of models as this one. + clone: function() { + return new this.constructor(this.models, { + model: this.model, + comparator: this.comparator + }); + }, + + // Define how to uniquely identify models in the collection. + modelId: function(attrs) { + return attrs[this.model.prototype.idAttribute || 'id']; + }, + + // Get an iterator of all models in this collection. + values: function() { + return new CollectionIterator(this, ITERATOR_VALUES); + }, + + // Get an iterator of all model IDs in this collection. + keys: function() { + return new CollectionIterator(this, ITERATOR_KEYS); + }, + + // Get an iterator of all [ID, model] tuples in this collection. + entries: function() { + return new CollectionIterator(this, ITERATOR_KEYSVALUES); + }, + + // Private method to reset all internal state. Called when the collection + // is first initialized or reset. + _reset: function() { + this.length = 0; + this.models = []; + this._byId = {}; + }, + + // Prepare a hash of attributes (or other model) to be added to this + // collection. + _prepareModel: function(attrs, options) { + if (this._isModel(attrs)) { + if (!attrs.collection) attrs.collection = this; + return attrs; + } + options = options ? _.clone(options) : {}; + options.collection = this; + var model = new this.model(attrs, options); + if (!model.validationError) return model; + this.trigger('invalid', this, model.validationError, options); + return false; + }, + + // Internal method called by both remove and set. + _removeModels: function(models, options) { + var removed = []; + for (var i = 0; i < models.length; i++) { + var model = this.get(models[i]); + if (!model) continue; + + var index = this.indexOf(model); + this.models.splice(index, 1); + this.length--; + + // Remove references before triggering 'remove' event to prevent an + // infinite loop. #3693 + delete this._byId[model.cid]; + var id = this.modelId(model.attributes); + if (id != null) delete this._byId[id]; + + if (!options.silent) { + options.index = index; + model.trigger('remove', model, this, options); + } + + removed.push(model); + this._removeReference(model, options); + } + return removed; + }, + + // Method for checking whether an object should be considered a model for + // the purposes of adding to the collection. + _isModel: function(model) { + return model instanceof Model; + }, + + // Internal method to create a model's ties to a collection. + _addReference: function(model, options) { + this._byId[model.cid] = model; + var id = this.modelId(model.attributes); + if (id != null) this._byId[id] = model; + model.on('all', this._onModelEvent, this); + }, + + // Internal method to sever a model's ties to a collection. + _removeReference: function(model, options) { + delete this._byId[model.cid]; + var id = this.modelId(model.attributes); + if (id != null) delete this._byId[id]; + if (this === model.collection) delete model.collection; + model.off('all', this._onModelEvent, this); + }, + + // Internal method called every time a model in the set fires an event. + // Sets need to update their indexes when models change ids. All other + // events simply proxy through. "add" and "remove" events that originate + // in other collections are ignored. + _onModelEvent: function(event, model, collection, options) { + if (model) { + if ((event === 'add' || event === 'remove') && collection !== this) return; + if (event === 'destroy') this.remove(model, options); + if (event === 'change') { + var prevId = this.modelId(model.previousAttributes()); + var id = this.modelId(model.attributes); + if (prevId !== id) { + if (prevId != null) delete this._byId[prevId]; + if (id != null) this._byId[id] = model; + } + } + } + this.trigger.apply(this, arguments); + } + + }); + + // Defining an @@iterator method implements JavaScript's Iterable protocol. + // In modern ES2015 browsers, this value is found at Symbol.iterator. + /* global Symbol */ + var $$iterator = typeof Symbol === 'function' && Symbol.iterator; + if ($$iterator) { + Collection.prototype[$$iterator] = Collection.prototype.values; + } + + // CollectionIterator + // ------------------ + + // A CollectionIterator implements JavaScript's Iterator protocol, allowing the + // use of `for of` loops in modern browsers and interoperation between + // Backbone.Collection and other JavaScript functions and third-party libraries + // which can operate on Iterables. + var CollectionIterator = function(collection, kind) { + this._collection = collection; + this._kind = kind; + this._index = 0; + }; + + // This "enum" defines the three possible kinds of values which can be emitted + // by a CollectionIterator that correspond to the values(), keys() and entries() + // methods on Collection, respectively. + var ITERATOR_VALUES = 1; + var ITERATOR_KEYS = 2; + var ITERATOR_KEYSVALUES = 3; + + // All Iterators should themselves be Iterable. + if ($$iterator) { + CollectionIterator.prototype[$$iterator] = function() { + return this; + }; + } + + CollectionIterator.prototype.next = function() { + if (this._collection) { + + // Only continue iterating if the iterated collection is long enough. + if (this._index < this._collection.length) { + var model = this._collection.at(this._index); + this._index++; + + // Construct a value depending on what kind of values should be iterated. + var value; + if (this._kind === ITERATOR_VALUES) { + value = model; + } else { + var id = this._collection.modelId(model.attributes); + if (this._kind === ITERATOR_KEYS) { + value = id; + } else { // ITERATOR_KEYSVALUES + value = [id, model]; + } + } + return {value: value, done: false}; + } + + // Once exhausted, remove the reference to the collection so future + // calls to the next method always return done. + this._collection = void 0; + } + + return {value: void 0, done: true}; + }; + + // Backbone.View + // ------------- + + // Backbone Views are almost more convention than they are actual code. A View + // is simply a JavaScript object that represents a logical chunk of UI in the + // DOM. This might be a single item, an entire list, a sidebar or panel, or + // even the surrounding frame which wraps your whole app. Defining a chunk of + // UI as a **View** allows you to define your DOM events declaratively, without + // having to worry about render order ... and makes it easy for the view to + // react to specific changes in the state of your models. + + // Creating a Backbone.View creates its initial element outside of the DOM, + // if an existing element is not provided... + var View = Backbone.View = function(options) { + this.cid = _.uniqueId('view'); + this.preinitialize.apply(this, arguments); + _.extend(this, _.pick(options, viewOptions)); + this._ensureElement(); + this.initialize.apply(this, arguments); + }; + + // Cached regex to split keys for `delegate`. + var delegateEventSplitter = /^(\S+)\s*(.*)$/; + + // List of view options to be set as properties. + var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; + + // Set up all inheritable **Backbone.View** properties and methods. + _.extend(View.prototype, Events, { + + // The default `tagName` of a View's element is `"div"`. + tagName: 'div', + + // jQuery delegate for element lookup, scoped to DOM elements within the + // current view. This should be preferred to global lookups where possible. + $: function(selector) { + return this.$el.find(selector); + }, + + // preinitialize is an empty function by default. You can override it with a function + // or object. preinitialize will run before any instantiation logic is run in the View + preinitialize: function(){}, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // **render** is the core function that your view should override, in order + // to populate its element (`this.el`), with the appropriate HTML. The + // convention is for **render** to always return `this`. + render: function() { + return this; + }, + + // Remove this view by taking the element out of the DOM, and removing any + // applicable Backbone.Events listeners. + remove: function() { + this._removeElement(); + this.stopListening(); + return this; + }, + + // Remove this view's element from the document and all event listeners + // attached to it. Exposed for subclasses using an alternative DOM + // manipulation API. + _removeElement: function() { + this.$el.remove(); + }, + + // Change the view's element (`this.el` property) and re-delegate the + // view's events on the new element. + setElement: function(element) { + this.undelegateEvents(); + this._setElement(element); + this.delegateEvents(); + return this; + }, + + // Creates the `this.el` and `this.$el` references for this view using the + // given `el`. `el` can be a CSS selector or an HTML string, a jQuery + // context or an element. Subclasses can override this to utilize an + // alternative DOM manipulation API and are only required to set the + // `this.el` property. + _setElement: function(el) { + this.$el = el instanceof Backbone.$ ? el : Backbone.$(el); + this.el = this.$el[0]; + }, + + // Set callbacks, where `this.events` is a hash of + // + // *{"event selector": "callback"}* + // + // { + // 'mousedown .title': 'edit', + // 'click .button': 'save', + // 'click .open': function(e) { ... } + // } + // + // pairs. Callbacks will be bound to the view, with `this` set properly. + // Uses event delegation for efficiency. + // Omitting the selector binds the event to `this.el`. + delegateEvents: function(events) { + events || (events = _.result(this, 'events')); + if (!events) return this; + this.undelegateEvents(); + for (var key in events) { + var method = events[key]; + if (!_.isFunction(method)) method = this[method]; + if (!method) continue; + var match = key.match(delegateEventSplitter); + this.delegate(match[1], match[2], method.bind(this)); + } + return this; + }, + + // Add a single event listener to the view's element (or a child element + // using `selector`). This only works for delegate-able events: not `focus`, + // `blur`, and not `change`, `submit`, and `reset` in Internet Explorer. + delegate: function(eventName, selector, listener) { + this.$el.on(eventName + '.delegateEvents' + this.cid, selector, listener); + return this; + }, + + // Clears all callbacks previously bound to the view by `delegateEvents`. + // You usually don't need to use this, but may wish to if you have multiple + // Backbone views attached to the same DOM element. + undelegateEvents: function() { + if (this.$el) this.$el.off('.delegateEvents' + this.cid); + return this; + }, + + // A finer-grained `undelegateEvents` for removing a single delegated event. + // `selector` and `listener` are both optional. + undelegate: function(eventName, selector, listener) { + this.$el.off(eventName + '.delegateEvents' + this.cid, selector, listener); + return this; + }, + + // Produces a DOM element to be assigned to your view. Exposed for + // subclasses using an alternative DOM manipulation API. + _createElement: function(tagName) { + return document.createElement(tagName); + }, + + // Ensure that the View has a DOM element to render into. + // If `this.el` is a string, pass it through `$()`, take the first + // matching element, and re-assign it to `el`. Otherwise, create + // an element from the `id`, `className` and `tagName` properties. + _ensureElement: function() { + if (!this.el) { + var attrs = _.extend({}, _.result(this, 'attributes')); + if (this.id) attrs.id = _.result(this, 'id'); + if (this.className) attrs['class'] = _.result(this, 'className'); + this.setElement(this._createElement(_.result(this, 'tagName'))); + this._setAttributes(attrs); + } else { + this.setElement(_.result(this, 'el')); + } + }, + + // Set attributes from a hash on this view's element. Exposed for + // subclasses using an alternative DOM manipulation API. + _setAttributes: function(attributes) { + this.$el.attr(attributes); + } + + }); + + // Proxy Backbone class methods to Underscore functions, wrapping the model's + // `attributes` object or collection's `models` array behind the scenes. + // + // collection.filter(function(model) { return model.get('age') > 10 }); + // collection.each(this.addView); + // + // `Function#apply` can be slow so we use the method's arg count, if we know it. + var addMethod = function(base, length, method, attribute) { + switch (length) { + case 1: return function() { + return base[method](this[attribute]); + }; + case 2: return function(value) { + return base[method](this[attribute], value); + }; + case 3: return function(iteratee, context) { + return base[method](this[attribute], cb(iteratee, this), context); + }; + case 4: return function(iteratee, defaultVal, context) { + return base[method](this[attribute], cb(iteratee, this), defaultVal, context); + }; + default: return function() { + var args = slice.call(arguments); + args.unshift(this[attribute]); + return base[method].apply(base, args); + }; + } + }; + + var addUnderscoreMethods = function(Class, base, methods, attribute) { + _.each(methods, function(length, method) { + if (base[method]) Class.prototype[method] = addMethod(base, length, method, attribute); + }); + }; + + // Support `collection.sortBy('attr')` and `collection.findWhere({id: 1})`. + var cb = function(iteratee, instance) { + if (_.isFunction(iteratee)) return iteratee; + if (_.isObject(iteratee) && !instance._isModel(iteratee)) return modelMatcher(iteratee); + if (_.isString(iteratee)) return function(model) { return model.get(iteratee); }; + return iteratee; + }; + var modelMatcher = function(attrs) { + var matcher = _.matches(attrs); + return function(model) { + return matcher(model.attributes); + }; + }; + + // Underscore methods that we want to implement on the Collection. + // 90% of the core usefulness of Backbone Collections is actually implemented + // right here: + var collectionMethods = {forEach: 3, each: 3, map: 3, collect: 3, reduce: 0, + foldl: 0, inject: 0, reduceRight: 0, foldr: 0, find: 3, detect: 3, filter: 3, + select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3, + contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3, + head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3, + without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3, + isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3, + sortBy: 3, indexBy: 3, findIndex: 3, findLastIndex: 3}; + + + // Underscore methods that we want to implement on the Model, mapped to the + // number of arguments they take. + var modelMethods = {keys: 1, values: 1, pairs: 1, invert: 1, pick: 0, + omit: 0, chain: 1, isEmpty: 1}; + + // Mix in each Underscore method as a proxy to `Collection#models`. + + _.each([ + [Collection, collectionMethods, 'models'], + [Model, modelMethods, 'attributes'] + ], function(config) { + var Base = config[0], + methods = config[1], + attribute = config[2]; + + Base.mixin = function(obj) { + var mappings = _.reduce(_.functions(obj), function(memo, name) { + memo[name] = 0; + return memo; + }, {}); + addUnderscoreMethods(Base, obj, mappings, attribute); + }; + + addUnderscoreMethods(Base, _, methods, attribute); + }); + + // Backbone.sync + // ------------- + + // Override this function to change the manner in which Backbone persists + // models to the server. You will be passed the type of request, and the + // model in question. By default, makes a RESTful Ajax request + // to the model's `url()`. Some possible customizations could be: + // + // * Use `setTimeout` to batch rapid-fire updates into a single request. + // * Send up the models as XML instead of JSON. + // * Persist models via WebSockets instead of Ajax. + // + // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests + // as `POST`, with a `_method` parameter containing the true HTTP method, + // as well as all requests with the body as `application/x-www-form-urlencoded` + // instead of `application/json` with the model in a param named `model`. + // Useful when interfacing with server-side languages like **PHP** that make + // it difficult to read the body of `PUT` requests. + Backbone.sync = function(method, model, options) { + var type = methodMap[method]; + + // Default options, unless specified. + _.defaults(options || (options = {}), { + emulateHTTP: Backbone.emulateHTTP, + emulateJSON: Backbone.emulateJSON + }); + + // Default JSON-request options. + var params = {type: type, dataType: 'json'}; + + // Ensure that we have a URL. + if (!options.url) { + params.url = _.result(model, 'url') || urlError(); + } + + // Ensure that we have the appropriate request data. + if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { + params.contentType = 'application/json'; + params.data = JSON.stringify(options.attrs || model.toJSON(options)); + } + + // For older servers, emulate JSON by encoding the request into an HTML-form. + if (options.emulateJSON) { + params.contentType = 'application/x-www-form-urlencoded'; + params.data = params.data ? {model: params.data} : {}; + } + + // For older servers, emulate HTTP by mimicking the HTTP method with `_method` + // And an `X-HTTP-Method-Override` header. + if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) { + params.type = 'POST'; + if (options.emulateJSON) params.data._method = type; + var beforeSend = options.beforeSend; + options.beforeSend = function(xhr) { + xhr.setRequestHeader('X-HTTP-Method-Override', type); + if (beforeSend) return beforeSend.apply(this, arguments); + }; + } + + // Don't process data on a non-GET request. + if (params.type !== 'GET' && !options.emulateJSON) { + params.processData = false; + } + + // Pass along `textStatus` and `errorThrown` from jQuery. + var error = options.error; + options.error = function(xhr, textStatus, errorThrown) { + options.textStatus = textStatus; + options.errorThrown = errorThrown; + if (error) error.call(options.context, xhr, textStatus, errorThrown); + }; + + // Make the request, allowing the user to override any Ajax options. + var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); + model.trigger('request', model, xhr, options); + return xhr; + }; + + // Map from CRUD to HTTP for our default `Backbone.sync` implementation. + var methodMap = { + create: 'POST', + update: 'PUT', + patch: 'PATCH', + delete: 'DELETE', + read: 'GET' + }; + + // Set the default implementation of `Backbone.ajax` to proxy through to `$`. + // Override this if you'd like to use a different library. + Backbone.ajax = function() { + return Backbone.$.ajax.apply(Backbone.$, arguments); + }; + + // Backbone.Router + // --------------- + + // Routers map faux-URLs to actions, and fire events when routes are + // matched. Creating a new one sets its `routes` hash, if not set statically. + var Router = Backbone.Router = function(options) { + options || (options = {}); + this.preinitialize.apply(this, arguments); + if (options.routes) this.routes = options.routes; + this._bindRoutes(); + this.initialize.apply(this, arguments); + }; + + // Cached regular expressions for matching named param parts and splatted + // parts of route strings. + var optionalParam = /\((.*?)\)/g; + var namedParam = /(\(\?)?:\w+/g; + var splatParam = /\*\w+/g; + var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; + + // Set up all inheritable **Backbone.Router** properties and methods. + _.extend(Router.prototype, Events, { + + // preinitialize is an empty function by default. You can override it with a function + // or object. preinitialize will run before any instantiation logic is run in the Router. + preinitialize: function(){}, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // Manually bind a single named route to a callback. For example: + // + // this.route('search/:query/p:num', 'search', function(query, num) { + // ... + // }); + // + route: function(route, name, callback) { + if (!_.isRegExp(route)) route = this._routeToRegExp(route); + if (_.isFunction(name)) { + callback = name; + name = ''; + } + if (!callback) callback = this[name]; + var router = this; + Backbone.history.route(route, function(fragment) { + var args = router._extractParameters(route, fragment); + if (router.execute(callback, args, name) !== false) { + router.trigger.apply(router, ['route:' + name].concat(args)); + router.trigger('route', name, args); + Backbone.history.trigger('route', router, name, args); + } + }); + return this; + }, + + // Execute a route handler with the provided parameters. This is an + // excellent place to do pre-route setup or post-route cleanup. + execute: function(callback, args, name) { + if (callback) callback.apply(this, args); + }, + + // Simple proxy to `Backbone.history` to save a fragment into the history. + navigate: function(fragment, options) { + Backbone.history.navigate(fragment, options); + return this; + }, + + // Bind all defined routes to `Backbone.history`. We have to reverse the + // order of the routes here to support behavior where the most general + // routes can be defined at the bottom of the route map. + _bindRoutes: function() { + if (!this.routes) return; + this.routes = _.result(this, 'routes'); + var route, routes = _.keys(this.routes); + while ((route = routes.pop()) != null) { + this.route(route, this.routes[route]); + } + }, + + // Convert a route string into a regular expression, suitable for matching + // against the current location hash. + _routeToRegExp: function(route) { + route = route.replace(escapeRegExp, '\\$&') + .replace(optionalParam, '(?:$1)?') + .replace(namedParam, function(match, optional) { + return optional ? match : '([^/?]+)'; + }) + .replace(splatParam, '([^?]*?)'); + return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'); + }, + + // Given a route, and a URL fragment that it matches, return the array of + // extracted decoded parameters. Empty or unmatched parameters will be + // treated as `null` to normalize cross-browser behavior. + _extractParameters: function(route, fragment) { + var params = route.exec(fragment).slice(1); + return _.map(params, function(param, i) { + // Don't decode the search params. + if (i === params.length - 1) return param || null; + return param ? decodeURIComponent(param) : null; + }); + } + + }); + + // Backbone.History + // ---------------- + + // Handles cross-browser history management, based on either + // [pushState](http://diveintohtml5.info/history.html) and real URLs, or + // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange) + // and URL fragments. If the browser supports neither (old IE, natch), + // falls back to polling. + var History = Backbone.History = function() { + this.handlers = []; + this.checkUrl = this.checkUrl.bind(this); + + // Ensure that `History` can be used outside of the browser. + if (typeof window !== 'undefined') { + this.location = window.location; + this.history = window.history; + } + }; + + // Cached regex for stripping a leading hash/slash and trailing space. + var routeStripper = /^[#\/]|\s+$/g; + + // Cached regex for stripping leading and trailing slashes. + var rootStripper = /^\/+|\/+$/g; + + // Cached regex for stripping urls of hash. + var pathStripper = /#.*$/; + + // Has the history handling already been started? + History.started = false; + + // Set up all inheritable **Backbone.History** properties and methods. + _.extend(History.prototype, Events, { + + // The default interval to poll for hash changes, if necessary, is + // twenty times a second. + interval: 50, + + // Are we at the app root? + atRoot: function() { + var path = this.location.pathname.replace(/[^\/]$/, '$&/'); + return path === this.root && !this.getSearch(); + }, + + // Does the pathname match the root? + matchRoot: function() { + var path = this.decodeFragment(this.location.pathname); + var rootPath = path.slice(0, this.root.length - 1) + '/'; + return rootPath === this.root; + }, + + // Unicode characters in `location.pathname` are percent encoded so they're + // decoded for comparison. `%25` should not be decoded since it may be part + // of an encoded parameter. + decodeFragment: function(fragment) { + return decodeURI(fragment.replace(/%25/g, '%2525')); + }, + + // In IE6, the hash fragment and search params are incorrect if the + // fragment contains `?`. + getSearch: function() { + var match = this.location.href.replace(/#.*/, '').match(/\?.+/); + return match ? match[0] : ''; + }, + + // Gets the true hash value. Cannot use location.hash directly due to bug + // in Firefox where location.hash will always be decoded. + getHash: function(window) { + var match = (window || this).location.href.match(/#(.*)$/); + return match ? match[1] : ''; + }, + + // Get the pathname and search params, without the root. + getPath: function() { + var path = this.decodeFragment( + this.location.pathname + this.getSearch() + ).slice(this.root.length - 1); + return path.charAt(0) === '/' ? path.slice(1) : path; + }, + + // Get the cross-browser normalized URL fragment from the path or hash. + getFragment: function(fragment) { + if (fragment == null) { + if (this._usePushState || !this._wantsHashChange) { + fragment = this.getPath(); + } else { + fragment = this.getHash(); + } + } + return fragment.replace(routeStripper, ''); + }, + + // Start the hash change handling, returning `true` if the current URL matches + // an existing route, and `false` otherwise. + start: function(options) { + if (History.started) throw new Error('Backbone.history has already been started'); + History.started = true; + + // Figure out the initial configuration. Do we need an iframe? + // Is pushState desired ... is it available? + this.options = _.extend({root: '/'}, this.options, options); + this.root = this.options.root; + this._wantsHashChange = this.options.hashChange !== false; + this._hasHashChange = 'onhashchange' in window && (document.documentMode === void 0 || document.documentMode > 7); + this._useHashChange = this._wantsHashChange && this._hasHashChange; + this._wantsPushState = !!this.options.pushState; + this._hasPushState = !!(this.history && this.history.pushState); + this._usePushState = this._wantsPushState && this._hasPushState; + this.fragment = this.getFragment(); + + // Normalize root to always include a leading and trailing slash. + this.root = ('/' + this.root + '/').replace(rootStripper, '/'); + + // Transition from hashChange to pushState or vice versa if both are + // requested. + if (this._wantsHashChange && this._wantsPushState) { + + // If we've started off with a route from a `pushState`-enabled + // browser, but we're currently in a browser that doesn't support it... + if (!this._hasPushState && !this.atRoot()) { + var rootPath = this.root.slice(0, -1) || '/'; + this.location.replace(rootPath + '#' + this.getPath()); + // Return immediately as browser will do redirect to new url + return true; + + // Or if we've started out with a hash-based route, but we're currently + // in a browser where it could be `pushState`-based instead... + } else if (this._hasPushState && this.atRoot()) { + this.navigate(this.getHash(), {replace: true}); + } + + } + + // Proxy an iframe to handle location events if the browser doesn't + // support the `hashchange` event, HTML5 history, or the user wants + // `hashChange` but not `pushState`. + if (!this._hasHashChange && this._wantsHashChange && !this._usePushState) { + this.iframe = document.createElement('iframe'); + this.iframe.src = 'javascript:0'; + this.iframe.style.display = 'none'; + this.iframe.tabIndex = -1; + var body = document.body; + // Using `appendChild` will throw on IE < 9 if the document is not ready. + var iWindow = body.insertBefore(this.iframe, body.firstChild).contentWindow; + iWindow.document.open(); + iWindow.document.close(); + iWindow.location.hash = '#' + this.fragment; + } + + // Add a cross-platform `addEventListener` shim for older browsers. + var addEventListener = window.addEventListener || function(eventName, listener) { + return attachEvent('on' + eventName, listener); + }; + + // Depending on whether we're using pushState or hashes, and whether + // 'onhashchange' is supported, determine how we check the URL state. + if (this._usePushState) { + addEventListener('popstate', this.checkUrl, false); + } else if (this._useHashChange && !this.iframe) { + addEventListener('hashchange', this.checkUrl, false); + } else if (this._wantsHashChange) { + this._checkUrlInterval = setInterval(this.checkUrl, this.interval); + } + + if (!this.options.silent) return this.loadUrl(); + }, + + // Disable Backbone.history, perhaps temporarily. Not useful in a real app, + // but possibly useful for unit testing Routers. + stop: function() { + // Add a cross-platform `removeEventListener` shim for older browsers. + var removeEventListener = window.removeEventListener || function(eventName, listener) { + return detachEvent('on' + eventName, listener); + }; + + // Remove window listeners. + if (this._usePushState) { + removeEventListener('popstate', this.checkUrl, false); + } else if (this._useHashChange && !this.iframe) { + removeEventListener('hashchange', this.checkUrl, false); + } + + // Clean up the iframe if necessary. + if (this.iframe) { + document.body.removeChild(this.iframe); + this.iframe = null; + } + + // Some environments will throw when clearing an undefined interval. + if (this._checkUrlInterval) clearInterval(this._checkUrlInterval); + History.started = false; + }, + + // Add a route to be tested when the fragment changes. Routes added later + // may override previous routes. + route: function(route, callback) { + this.handlers.unshift({route: route, callback: callback}); + }, + + // Checks the current URL to see if it has changed, and if it has, + // calls `loadUrl`, normalizing across the hidden iframe. + checkUrl: function(e) { + var current = this.getFragment(); + + // If the user pressed the back button, the iframe's hash will have + // changed and we should use that for comparison. + if (current === this.fragment && this.iframe) { + current = this.getHash(this.iframe.contentWindow); + } + + if (current === this.fragment) return false; + if (this.iframe) this.navigate(current); + this.loadUrl(); + }, + + // Attempt to load the current URL fragment. If a route succeeds with a + // match, returns `true`. If no defined routes matches the fragment, + // returns `false`. + loadUrl: function(fragment) { + // If the root doesn't match, no routes can match either. + if (!this.matchRoot()) return false; + fragment = this.fragment = this.getFragment(fragment); + return _.some(this.handlers, function(handler) { + if (handler.route.test(fragment)) { + handler.callback(fragment); + return true; + } + }); + }, + + // Save a fragment into the hash history, or replace the URL state if the + // 'replace' option is passed. You are responsible for properly URL-encoding + // the fragment in advance. + // + // The options object can contain `trigger: true` if you wish to have the + // route callback be fired (not usually desirable), or `replace: true`, if + // you wish to modify the current URL without adding an entry to the history. + navigate: function(fragment, options) { + if (!History.started) return false; + if (!options || options === true) options = {trigger: !!options}; + + // Normalize the fragment. + fragment = this.getFragment(fragment || ''); + + // Don't include a trailing slash on the root. + var rootPath = this.root; + if (fragment === '' || fragment.charAt(0) === '?') { + rootPath = rootPath.slice(0, -1) || '/'; + } + var url = rootPath + fragment; + + // Strip the fragment of the query and hash for matching. + fragment = fragment.replace(pathStripper, ''); + + // Decode for matching. + var decodedFragment = this.decodeFragment(fragment); + + if (this.fragment === decodedFragment) return; + this.fragment = decodedFragment; + + // If pushState is available, we use it to set the fragment as a real URL. + if (this._usePushState) { + this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url); + + // If hash changes haven't been explicitly disabled, update the hash + // fragment to store history. + } else if (this._wantsHashChange) { + this._updateHash(this.location, fragment, options.replace); + if (this.iframe && fragment !== this.getHash(this.iframe.contentWindow)) { + var iWindow = this.iframe.contentWindow; + + // Opening and closing the iframe tricks IE7 and earlier to push a + // history entry on hash-tag change. When replace is true, we don't + // want this. + if (!options.replace) { + iWindow.document.open(); + iWindow.document.close(); + } + + this._updateHash(iWindow.location, fragment, options.replace); + } + + // If you've told us that you explicitly don't want fallback hashchange- + // based history, then `navigate` becomes a page refresh. + } else { + return this.location.assign(url); + } + if (options.trigger) return this.loadUrl(fragment); + }, + + // Update the hash location, either replacing the current entry, or adding + // a new one to the browser history. + _updateHash: function(location, fragment, replace) { + if (replace) { + var href = location.href.replace(/(javascript:|#).*$/, ''); + location.replace(href + '#' + fragment); + } else { + // Some browsers require that `hash` contains a leading #. + location.hash = '#' + fragment; + } + } + + }); + + // Create the default Backbone.history. + Backbone.history = new History; + + // Helpers + // ------- + + // Helper function to correctly set up the prototype chain for subclasses. + // Similar to `goog.inherits`, but uses a hash of prototype properties and + // class properties to be extended. + var extend = function(protoProps, staticProps) { + var parent = this; + var child; + + // The constructor function for the new subclass is either defined by you + // (the "constructor" property in your `extend` definition), or defaulted + // by us to simply call the parent constructor. + if (protoProps && _.has(protoProps, 'constructor')) { + child = protoProps.constructor; + } else { + child = function(){ return parent.apply(this, arguments); }; + } + + // Add static properties to the constructor function, if supplied. + _.extend(child, parent, staticProps); + + // Set the prototype chain to inherit from `parent`, without calling + // `parent`'s constructor function and add the prototype properties. + child.prototype = _.create(parent.prototype, protoProps); + child.prototype.constructor = child; + + // Set a convenience property in case the parent's prototype is needed + // later. + child.__super__ = parent.prototype; + + return child; + }; + + // Set up inheritance for the model, collection, router, view and history. + Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend; + + // Throw an error when a URL is needed, and none is supplied. + var urlError = function() { + throw new Error('A "url" property or function must be specified'); + }; + + // Wrap an optional error callback with a fallback error event. + var wrapError = function(model, options) { + var error = options.error; + options.error = function(resp) { + if (error) error.call(options.context, model, resp, options); + model.trigger('error', model, resp, options); + }; + }; + + return Backbone; +}); diff --git a/openid-connect-server-webapp/src/main/webapp/resources/js/lib/jquery.js b/openid-connect-server-webapp/src/main/webapp/resources/js/lib/jquery.js index 6feb11086f..b9f8937fdd 100644 --- a/openid-connect-server-webapp/src/main/webapp/resources/js/lib/jquery.js +++ b/openid-connect-server-webapp/src/main/webapp/resources/js/lib/jquery.js @@ -1,27 +1,29 @@ /*! - * jQuery JavaScript Library v1.11.3 - * http://jquery.com/ + * jQuery JavaScript Library v3.6.0 + * https://jquery.com/ * * Includes Sizzle.js - * http://sizzlejs.com/ + * https://sizzlejs.com/ * - * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Copyright OpenJS Foundation and other contributors * Released under the MIT license - * http://jquery.org/license + * https://jquery.org/license * - * Date: 2015-04-28T16:19Z + * Date: 2021-03-02T17:08Z */ +( function( global, factory ) { -(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 + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a 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 + // See ticket #14549 for more info. module.exports = global.document ? factory( global, true ) : function( w ) { @@ -35,5194 +37,5123 @@ } // Pass this if window is not defined yet -}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { +} )( 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+ -// +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. + "use strict"; -var deletedIds = []; + var arr = []; -var slice = deletedIds.slice; + var getProto = Object.getPrototypeOf; -var concat = deletedIds.concat; + var slice = arr.slice; + + var flat = arr.flat ? function( array ) { + return arr.flat.call( array ); + } : function( array ) { + return arr.concat.apply( [], array ); + }; -var push = deletedIds.push; -var indexOf = deletedIds.indexOf; + var push = arr.push; -var class2type = {}; + var indexOf = arr.indexOf; -var toString = class2type.toString; + var class2type = {}; -var hasOwn = class2type.hasOwnProperty; + var toString = class2type.toString; -var support = {}; + var hasOwn = class2type.hasOwnProperty; + var fnToString = hasOwn.toString; + var ObjectFunctionString = fnToString.call( Object ); -var - version = "1.11.3", + var support = {}; - // 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 ); - }, + var isFunction = function isFunction( obj ) { - // Support: Android<4.1, IE<9 - // Make sure we trim BOM and NBSP - rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + // Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5 + // Plus for old WebKit, typeof returns "function" for HTML collections + // (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756) + return typeof obj === "function" && typeof obj.nodeType !== "number" && + typeof obj.item !== "function"; + }; - // 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(); + var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; }; -jQuery.fn = jQuery.prototype = { - // The current version of jQuery being used - jquery: version, - constructor: jQuery, + var document = window.document; - // Start with an empty selector - selector: "", - // The default length of a jQuery object is 0 - length: 0, - toArray: function() { - return slice.call( this ); - }, + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; - // 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 ? + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } - // 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 ); - }, + function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; + } + /* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module - // 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: deletedIds.sort, - splice: deletedIds.splice -}; - -jQuery.extend = jQuery.fn.extend = function() { - var src, copyIsArray, copy, name, options, 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++; - } + var + version = "3.6.0", - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } + // Define a local copy of jQuery + jQuery = function( selector, context ) { - // extend jQuery itself if only one argument is passed - if ( i === length ) { - target = this; - i--; - } + // 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 ); + }; - 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 ]; + jQuery.fn = jQuery.prototype = { - // Prevent never-ending loop - if ( target === copy ) { - continue; - } + // The current version of jQuery being used + jquery: version, - // 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 : []; + constructor: jQuery, - } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; - } + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); } - } - } - // Return the modified object - return target; -}; + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, -jQuery.extend({ - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { - // Assume jQuery is ready without the ready module - isReady: true, + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); - error: function( msg ) { - throw new Error( msg ); - }, + // Add the old object onto the stack (as a reference) + ret.prevObject = this; - noop: function() {}, + // Return the newly-formed element set + return ret; + }, - // 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"; - }, + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, - isArray: Array.isArray || function( obj ) { - return jQuery.type(obj) === "array"; - }, + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, - isWindow: function( obj ) { - /* jshint eqeqeq: false */ - return obj != null && obj == obj.window; - }, + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, - 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 - // adding 1 corrects loss of precision from parseFloat (#15100) - return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0; - }, + first: function() { + return this.eq( 0 ); + }, - isEmptyObject: function( obj ) { - var name; - for ( name in obj ) { - return false; - } - return true; - }, + last: function() { + return this.eq( -1 ); + }, - isPlainObject: function( obj ) { - var key; + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, - try { - // Not own constructor property must be Object - if ( obj.constructor && - !hasOwn.call(obj, "constructor") && - !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - } catch ( e ) { - // IE8,9 Will throw exceptions on certain host objects #9897 - return false; - } + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, - // Support: IE<9 - // Handle iteration over inherited properties before own properties. - if ( support.ownLast ) { - for ( key in obj ) { - return hasOwn.call( obj, key ); - } - } + end: function() { + return this.prevObject || this.constructor(); + }, - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - for ( key in obj ) {} + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice + }; - return key === undefined || hasOwn.call( obj, key ); - }, + jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; - type: function( obj ) { - if ( obj == null ) { - return obj + ""; + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; } - return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call(obj) ] || "object" : - typeof obj; - }, - - // Evaluates a script in a global context - // Workarounds based on findings by Jim Driscoll - // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context - globalEval: function( data ) { - if ( data && jQuery.trim( data ) ) { - // We use execScript on Internet Explorer - // We use an anonymous function so that context is window - // rather than jQuery in Firefox - ( window.execScript || function( data ) { - window[ "eval" ].call( window, data ); - } )( data ); - } - }, - - // 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(); - }, + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } - // args is for internal usage only - each: function( obj, callback, args ) { - var value, - i = 0, - length = obj.length, - isArray = isArraylike( obj ); + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } - if ( args ) { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.apply( obj[ i ], args ); + for ( ; i < length; i++ ) { - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.apply( obj[ i ], args ); + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { - if ( value === false ) { - break; + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; } - } - } - // 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 ] ); + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.call( obj[ i ], i, obj[ i ] ); + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; - if ( value === false ) { - break; + // 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 obj; - }, + // Return the modified object + return target; + }; - // Support: Android<4.1, IE<9 - trim: function( text ) { - return text == null ? - "" : - ( text + "" ).replace( rtrim, "" ); - }, + jQuery.extend( { - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - if ( arr != null ) { - if ( isArraylike( Object(arr) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - push.call( ret, arr ); - } - } + // Assume jQuery is ready without the ready module + isReady: true, - return ret; - }, + error: function( msg ) { + throw new Error( msg ); + }, - inArray: function( elem, arr, i ) { - var len; + noop: function() {}, - if ( arr ) { - if ( indexOf ) { - return indexOf.call( arr, elem, i ); + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; } - len = arr.length; - i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + proto = getProto( obj ); - for ( ; i < len; i++ ) { - // Skip accessing in sparse arrays - if ( i in arr && arr[ i ] === elem ) { - return i; - } + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; } - } - - return -1; - }, - merge: function( first, second ) { - var len = +second.length, - j = 0, - i = first.length; + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, - while ( j < len ) { - first[ i++ ] = second[ j++ ]; - } + isEmptyObject: function( obj ) { + var name; - // Support: IE<9 - // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists) - if ( len !== len ) { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; + for ( name in obj ) { + return false; } - } + return true; + }, - first.length = i; + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, - return first; - }, + each: function( obj, callback ) { + var length, i = 0; - grep: function( elems, callback, invert ) { - var callbackInverse, - matches = [], - i = 0, - length = elems.length, - callbackExpect = !invert; + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } - // 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 obj; + }, + + // 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 matches; - }, + return ret; + }, - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, - i = 0, - length = elems.length, - isArray = isArraylike( elems ), - ret = []; + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, - // 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 ); + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; - if ( value != null ) { - ret.push( value ); - } + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; } - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); + first.length = i; + + return first; + }, - if ( value != null ) { - ret.push( value ); + 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 ] ); } } - } - // Flatten any nested arrays - return concat.apply( [], ret ); - }, + return matches; + }, - // A global GUID counter for objects - guid: 1, + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - var args, proxy, tmp; + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); - if ( typeof context === "string" ) { - tmp = fn[ context ]; - context = fn; - fn = tmp; - } + if ( value != null ) { + ret.push( value ); + } + } - // 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; - } + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); - // Simulated bind - args = slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); - }; + if ( value != null ) { + ret.push( value ); + } + } + } - // 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++; + // Flatten any nested arrays + return flat( ret ); + }, - return proxy; - }, + // A global GUID counter for objects + guid: 1, - now: function() { - return +( new Date() ); - }, + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support + } ); - // jQuery.support is not used in Core but other projects attach their - // properties to it so it needs to exist. - support: support -}); + if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; + } // Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); + jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), + function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); + } ); -function isArraylike( obj ) { + function isArrayLike( obj ) { - // Support: iOS 8.2 (not reproducible in simulator) - // `in` check used to prevent JIT error (gh-2145) - // hasOwn isn't used here due to false negatives - // regarding Nodelist length in IE - var length = "length" in obj && obj.length, - type = jQuery.type( obj ); + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); - if ( type === "function" || jQuery.isWindow( obj ) ) { - return false; - } + if ( isFunction( obj ) || 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; } - - return type === "array" || length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj; -} -var Sizzle = -/*! - * Sizzle CSS Selector Engine v2.2.0-pre - * http://sizzlejs.com/ + var Sizzle = + /*! + * Sizzle CSS Selector Engine v2.3.6 + * https://sizzlejs.com/ * - * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors + * Copyright JS Foundation and other contributors * Released under the MIT license - * http://jquery.org/license + * https://js.foundation/ * - * Date: 2014-12-16 + * Date: 2021-02-16 */ -(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" + 1 * 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 - 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 as it's faster than native - // http://jsperf.com/thor-indexof-vs-for/5 - indexOf = function( list, elem ) { - var i = 0, - len = list.length; - for ( ; i < len; i++ ) { - if ( list[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 - rwhitespace = new RegExp( whitespace + "+", "g" ), - 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 ); - }, - - // Used for iframes - // See setDocument() - // Removing the function wrapper causes a "Permission Denied" - // error in IE - unloadHandler = function() { - setDocument(); - }; + ( 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" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, -// 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; - } - }; -} + // Instance methods + hasOwn = ( {} ).hasOwnProperty, + arr = [], + pop = arr.pop, + pushNative = arr.push, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[ i ] === elem ) { + return i; + } + } + return -1; + }, -function Sizzle( selector, context, results, seed ) { - var match, elem, m, nodeType, - // QSA vars - i, groups, old, nid, newContext, newSelector; + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + + "ismap|loop|multiple|open|readonly|required|scoped", - if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { - setDocument( context ); - } + // Regular expressions - context = context || document; - results = results || []; - nodeType = context.nodeType; + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", - if ( typeof selector !== "string" || !selector || - nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", - return results; - } + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + - if ( !seed && documentIsHTML ) { - - // Try to shortcut find operations when possible (e.g., not under DocumentFragment) - if ( nodeType !== 11 && (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; - } - } + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + - // Speed-up: Sizzle("TAG") - } else if ( match[2] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; + // "Attribute values must be CSS identifiers [capture 5] + // or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", - // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && support.getElementsByClassName ) { - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } + pseudos = ":(" + identifier + ")(?:\\((" + - // QSA path - if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - nid = old = expando; - newContext = context; - newSelector = nodeType !== 1 && selector; + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + - // 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 ); + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + - if ( (old = context.getAttribute("id")) ) { - nid = old.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - nid = "[id='" + nid + "'] "; + // 3. anything else (capture 2) + ".*" + + ")\\)|)", - i = groups.length; - while ( i-- ) { - groups[i] = nid + toSelector( groups[i] ); - } - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; - newSelector = groups.join(","); - } + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + + whitespace + "+$", "g" ), - if ( newSelector ) { - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); - } - } - } - } - } + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + "*" ), + rdescend = new RegExp( whitespace + "|>" ), - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); -} + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), -/** - * 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 = []; + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "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" ), - 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; -} + // 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" ) + }, -/** - * 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"); + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, - 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; - } -} + rnative = /^[^{]+\{\s*\[native \w/, -/** - * 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; + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - while ( i-- ) { - Expr.attrHandle[ arr[i] ] = handler; - } -} + rsibling = /[+~]/, -/** - * 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; - } + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + var high = "0x" + escape.slice( 1 ) - 0x10000; - // Check if b follows a - if ( cur ) { - while ( (cur = cur.nextSibling) ) { - if ( cur === b ) { - return -1; - } - } - } + return nonHex ? - return a ? 1 : -1; -} + // Strip the backslash prefix from a non-hex escape sequence + nonHex : -/** - * 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; - }; -} + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, -/** - * 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; - }; -} + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { -/** - * 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]); - } - } - }); - }); -} + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } -/** - * 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 !== "undefined" && 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, parent, - doc = node ? node.ownerDocument || node : preferredDoc; - - // 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; - parent = doc.defaultView; - - // 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", unloadHandler, false ); - } else if ( parent.attachEvent ) { - parent.attachEvent( "onunload", unloadHandler ); - } - } + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } - /* Support tests - ---------------------------------------------------------------------- */ - documentIsHTML = !isXML( doc ); + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, - /* Attributes - ---------------------------------------------------------------------- */ + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, - // 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"); - }); + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); - /* getElement(s)By* - ---------------------------------------------------------------------- */ +// Optimize for push.apply( _, NodeList ) + try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert(function( div ) { - div.appendChild( doc.createComment("") ); - return !div.getElementsByTagName("*").length; - }); - - // Support: IE<9 - support.getElementsByClassName = rnative.test( doc.getElementsByClassName ); - - // 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 !== "undefined" && 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 !== "undefined" && elem.getAttributeNode("id"); - return node && node.value === attrId; - }; - }; - } + // Support: Android<4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; + } catch ( e ) { + push = { apply: arr.length ? - // Tag - Expr.find["TAG"] = support.getElementsByTagName ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( tag ); + // Leverage slice if possible + function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + } : - // DocumentFragment nodes don't have gEBTN - } else if ( support.qsa ) { - return context.querySelectorAll( tag ); + // 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( tag, context ) { - var elem, - tmp = [], - i = 0, - // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too - results = context.getElementsByTagName( tag ); - // Filter out possible comments - if ( tag === "*" ) { - while ( (elem = results[i++]) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } + function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, - return tmp; - } - return results; - }; + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; - // Class - Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; + results = results || []; - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { - // QSA and matchesSelector support + return results; + } - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } - // 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 = []; + // Element context + } else { - 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 - docElem.appendChild( div ).innerHTML = "" + - ""; + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + contains( context, elem ) && + elem.id === m ) { - // 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("[msallowcapture^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); - } + results.push( elem ); + return results; + } + } - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if ( !div.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; - // Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+ - if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) { - rbuggyQSA.push("~="); - } + // Class selector + } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && + context.getElementsByClassName ) { - // 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"); - } + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } - // Support: Safari 8+, iOS 8+ - // https://bugs.webkit.org/show_bug.cgi?id=136851 - // In-page `selector#id sibing-combinator selector` fails - if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) { - rbuggyQSA.push(".#.+[+~]"); - } - }); + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && + + // Support: IE 8 only + // Exclude object elements + ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } - 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" ); + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } - // Support: IE8 - // Enforce case-sensitivity of name attribute - if ( div.querySelectorAll("[name=d]").length ) { - rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); - } + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } - // 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" ); + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); } - // Opera 10-11 does not throw on post-comma invalid pseudos - div.querySelectorAll("*,:x"); - rbuggyQSA.push(",.*:"); - }); - } + /** + * 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 = []; - 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 ); - }); - } + function cache( key, value ) { - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { - /* 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; + // Only keep the most recent entries + delete cache[ keys.shift() ]; } + return ( cache[ key + " " ] = value ); } + return cache; } - 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; + /** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ + function markFunction( fn ) { + fn[ expando ] = true; + return fn; } - // Maintain original order - return sortInput ? - ( indexOf( sortInput, a ) - indexOf( 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( sortInput, a ) - indexOf( 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] ) : + /** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ + function assert( fn ) { + var el = document.createElement( "fieldset" ); - // Otherwise nodes in our document sort first - ap[i] === preferredDoc ? -1 : - bp[i] === preferredDoc ? 1 : - 0; - }; + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { - return doc; -}; + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; + // release memory in IE + el = null; + } + } -Sizzle.matchesSelector = function( elem, expr ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } + /** + * 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 = arr.length; - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); + while ( i-- ) { + Expr.attrHandle[ arr[ i ] ] = handler; + } + } - if ( support.matchesSelector && documentIsHTML && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + /** + * 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 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } - try { - var ret = matches.call( elem, expr ); + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } - // 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; + return a ? 1 : -1; } - } catch (e) {} - } - return Sizzle( expr, document, null, [ elem ] ).length > 0; -}; + /** + * 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; + }; + } -Sizzle.contains = function( context, elem ) { - // Set document vars if needed - if ( ( context.ownerDocument || context ) !== document ) { - setDocument( context ); - } - return contains( context, elem ); -}; + /** + * 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; + }; + } -Sizzle.attr = function( elem, name ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } + /** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ + function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } - 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; -}; + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } -/** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ -Sizzle.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; + return elem.disabled === disabled; - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } - if ( hasDuplicate ) { - while ( (elem = results[i++]) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); + // Remaining elements are neither :enabled nor :disabled + return false; + }; } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; + /** + * 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; - return results; -}; + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); + } -/** - * 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 ); + /** + * 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 !== "undefined" && context; } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - return ret; -}; +// 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 ) { + var namespace = elem && elem.namespaceURI, + docElem = elem && ( elem.ownerDocument || elem ).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); + }; -Expr = Sizzle.selectors = { + /** + * 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, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } - // Can be adjusted by the user - cacheLength: 50, + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } - createPseudo: markFunction, + // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, + // Safari 4 - 5 only, Opera <=11.6 - 12.x only + // IE/Edge & older browsers don't support the :scope pseudo-class. + // Support: Safari 6.0 only + // Safari 6.0 supports :scope but it's an alias of :root there. + support.scope = assert( function( el ) { + docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); + return typeof el.querySelectorAll !== "undefined" && + !el.querySelectorAll( ":scope fieldset div" ).length; + } ); + + /* Attributes + ---------------------------------------------------------------------- */ - match: matchExpr, + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert( function( el ) { + el.className = "i"; + return !el.getAttribute( "className" ); + } ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ - attrHandle: {}, + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert( function( el ) { + el.appendChild( document.createComment( "" ) ); + return !el.getElementsByTagName( "*" ).length; + } ); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; - find: {}, + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, + if ( elem ) { - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( runescape, funescape ); + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } + return []; + } + }; + } - return match.slice( 0, 4 ); - }, + // Tag + Expr.find[ "TAG" ] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); - "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(); + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : - if ( match[1].slice( 0, 3 ) === "nth" ) { - // nth-* requires argument - if ( !match[3] ) { - Sizzle.error( match[0] ); - } + function( tag, context ) { + var elem, + tmp = [], + i = 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" ); + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); - // other types prohibit arguments - } else if ( match[3] ) { - Sizzle.error( match[0] ); - } + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } - return match; - }, + return tmp; + } + return results; + }; - "PSEUDO": function( match ) { - var excess, - unquoted = !match[6] && match[2]; + // Class + Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ - // Accept quoted arguments as-is - if ( match[3] ) { - match[2] = match[4] || match[5] || ""; + // 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 https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + var input; + + // 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 + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).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 + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } - // 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) ) { + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } - // excess is a negative index - match[0] = match[0].slice( 0, excess ); - match[2] = unquoted.slice( 0, excess ); - } + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } - filter: { + // 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 ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { return true; } : - function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } - "CLASS": function( className ) { - var pattern = classCache[ className + " " ]; + // Support: Firefox <=3.6 - 5 only + // Old Firefox doesn't throw on a badly-escaped identifier. + el.querySelectorAll( "\\\f" ); + rbuggyQSA.push( "[\\r\\n\\f]" ); + } ); + + assert( function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll( "[name=d]" ).length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } - return pattern || - (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && - classCache( className, function( elem ) { - return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); - }); - }, + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } - "ATTR": function( name, operator, check ) { - return function( elem ) { - var result = Sizzle.attr( elem, name ); + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; + // Support: Opera 10 - 11 only + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); } - result += ""; + if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { - 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.replace( rwhitespace, " " ) + " " ).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 ? + assert( function( el ) { - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); - 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; + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } - if ( parent ) { + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); - // :(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; - } + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // 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; } - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; } - return true; } + return false; + }; - start = [ forward ? parent.firstChild : parent.lastChild ]; + /* Sorting + ---------------------------------------------------------------------- */ - // 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 ]; + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { - while ( (node = ++nodeIndex && node && node[ dir ] || + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } - // Fallback to seeking `elem` from the start - (diff = nodeIndex = 0) || start.pop()) ) { + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - outerCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } + // Calculate position if both inputs belong to the same document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + 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 + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( a == document || a.ownerDocument == preferredDoc && + contains( preferredDoc, a ) ) { + return -1; } - // Use previously-cached element index if available - } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { - diff = cache[1]; + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( b == document || b.ownerDocument == preferredDoc && + contains( preferredDoc, b ) ) { + return 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 ]; - } + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } - if ( node === elem ) { - break; - } - } - } + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; } - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 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 ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + return a == document ? -1 : + b == document ? 1 : + /* eslint-enable eqeqeq */ + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } - "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( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); + // 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 ); } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - return fn; - } - }, + // Walk down the tree looking for a discrepancy + while ( ap[ i ] === bp[ i ] ) { + i++; + } - 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 i ? - return matcher[ expando ] ? - markFunction(function( seed, matches, context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[ i ], bp[ i ] ) : - // 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 ); - // Don't keep the element (issue #299) - input[0] = null; - return !results.pop(); - }; - }), + // Otherwise nodes in our document sort first + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + ap[ i ] == preferredDoc ? -1 : + bp[ i ] == preferredDoc ? 1 : + /* eslint-enable eqeqeq */ + 0; + }; - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; + return document; }; - }), - "contains": markFunction(function( text ) { - text = text.replace( runescape, funescape ); - 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; + Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); }; - }), - // Miscellaneous - "target": function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, + Sizzle.matchesSelector = function( elem, expr ) { + setDocument( elem ); - "root": function( elem ) { - return elem === docElem; - }, + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - "focus": function( elem ) { - return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); - }, + try { + var ret = matches.call( elem, expr ); - // Boolean properties - "enabled": function( elem ) { - return elem.disabled === false; - }, + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || - "disabled": function( elem ) { - return elem.disabled === true; - }, + // 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 ) { + nonnativeSelectorCache( expr, 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); - }, + return Sizzle( expr, document, null, [ elem ] ).length > 0; + }; - "selected": function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } + Sizzle.contains = function( context, elem ) { - return elem.selected === true; - }, + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( context.ownerDocument || context ) != document ) { + setDocument( context ); + } + return contains( context, elem ); + }; - // 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; + Sizzle.attr = function( elem, name ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( elem.ownerDocument || elem ) != document ) { + setDocument( elem ); } - } - return true; - }, - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, + var fn = Expr.attrHandle[ name.toLowerCase() ], - // Element/input types - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; + }; - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, + Sizzle.escape = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); + }; - "text": function( elem ) { - var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && + Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); + }; - // Support: IE<8 - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); - }, + /** + * 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 ); + } + } - // Position-in-collection - "first": createPositionalPseudo(function() { - return [ 0 ]; - }), + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; - "last": createPositionalPseudo(function( matchIndexes, length ) { - return [ length - 1 ]; - }), + return results; + }; - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), + /** + * 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; - "even": createPositionalPseudo(function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), + if ( !nodeType ) { - "odd": createPositionalPseudo(function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } -}; + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { -Expr.pseudos["nth"] = Expr.pseudos["eq"]; + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } -// 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 ); -} + // Do not include comment or processing instruction nodes -// Easy API for creating new setFilters -function setFilters() {} -setFilters.prototype = Expr.filters = Expr.pseudos; -Expr.setFilters = new setFilters(); + return ret; + }; -tokenize = Sizzle.tokenize = function( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; + Expr = Sizzle.selectors = { - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } + // Can be adjusted by the user + cacheLength: 50, - soFar = selector; - groups = []; - preFilters = Expr.preFilter; + createPseudo: markFunction, - while ( soFar ) { + match: matchExpr, - // 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 = []) ); - } + attrHandle: {}, - matched = false; + find: {}, - // 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 ); - } + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, - // 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 ); - } - } + preFilter: { + "ATTR": function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); - if ( !matched ) { - break; - } - } + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || + match[ 5 ] || "" ).replace( runescape, funescape ); - // 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; + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; } - } - } - } 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; - } - } - } - } - } - }; -} + return match.slice( 0, 4 ); + }, -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; -} + "CHILD": function( match ) { -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; + /* 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(); - for ( ; i < len; i++ ) { - if ( (elem = unmatched[i]) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { - return newUnmatched; -} + // nth-* requires argument + if ( !match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } -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, + // 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" ); - // Get initial elements from seed or context - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + // other types prohibit arguments + } else if ( match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, + return match; + }, - matcherOut = matcher ? - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + "PSEUDO": function( match ) { + var excess, + unquoted = !match[ 6 ] && match[ 2 ]; - // ...intermediate processing is necessary - [] : + if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + return null; + } - // ...otherwise use results directly - results : - matcherIn; + // Accept quoted arguments as-is + if ( match[ 3 ] ) { + match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && - // 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); - } - } - } + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { - 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) ); + // 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 ); } - 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( seed, elem ) : preMap[i]) > -1 ) { + filter: { - seed[temp] = !(results[temp] = elem); - } - } - } + "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 !== "undefined" && + 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; + } - // 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( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - // Avoid hanging onto element (issue #299) - checkContext = null; - return ret; - } ]; + result += ""; + + /* eslint-disable max-len */ + + 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.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + /* eslint-enable max-len */ + + }; + }, + + "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, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + 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; + } - 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 ); - } - } + start = [ forward ? parent.firstChild : parent.lastChild ]; - 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; - } - } + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { - // Track unmatched elements for set filters - if ( bySet ) { - // They will have gone through all possible matchers - if ( (elem = !matcher && elem) ) { - matchedCount--; - } + // Seek `elem` from a previously-cached index - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || ( node[ expando ] = {} ); - // Apply set filters to unmatched elements - matchedCount += i; - if ( bySet && i !== matchedCount ) { - j = 0; - while ( (matcher = setMatchers[j++]) ) { - matcher( unmatched, setMatched, context, xml ); - } + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); - 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 ); - } - } - } + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } + while ( ( node = ++nodeIndex && node && node[ dir ] || - // Add matches to results - push.apply( results, setMatched ); + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } - Sizzle.uniqueSort( results ); - } - } + } else { - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } + // Use previously-cached element index if available + if ( useCache ) { - return unmatched; - }; + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || ( node[ expando ] = {} ); - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); -compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } - 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 ); - } - } + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { - // Cache the compiled function - cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { - // 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) ); + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { - results = results || []; + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || + ( node[ expando ] = {} ); - // Try to minimize operations if there is no seed and only one group - if ( match.length === 1 ) { + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); - // 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 ] ) { + uniqueCache[ type ] = [ dirruns, diff ]; + } - context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; - if ( !context ) { - return results; + if ( node === elem ) { + break; + } + } + } + } + } - // Precompiled matchers will still verify ancestry, so step up a level - } else if ( compiled ) { - context = context.parentNode; - } + // 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 ); + } - selector = selector.slice( tokens.shift().value.length ); - } + // 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( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } - // 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; + return fn; } + }, - break; - } - } - } - } + pseudos: { - // 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; -}; + // Potentially complex pseudos + "not": markFunction( function( selector ) { -// One-time assignments + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); -// Sort stability -support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + return matcher[ expando ] ? + markFunction( function( seed, matches, _context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; -// Support: Chrome 14-35+ -// Always assume duplicates if they aren't passed to the comparison function -support.detectDuplicates = !!hasDuplicate; + // 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 ); + + // Don't keep the element (issue #299) + input[ 0 ] = null; + return !results.pop(); + }; + } ), + + "has": markFunction( function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + } ), + + "contains": markFunction( function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || 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": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( 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 ) { + // eslint-disable-next-line no-unused-expressions + elem.parentNode.selectedIndex; + } -// Initialize against the default document -setDocument(); + return elem.selected === true; + }, -// 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; -}); + // Contents + "empty": function( elem ) { -// 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 ); - } - }); -} + // 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; + } ), -// 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; - } - }); -} + "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 > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), -// 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; - } - }); -} + "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } + }; -return Sizzle; + Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; -})( window ); +// 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 + " " ]; -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; + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + while ( soFar ) { -var rneedsContext = jQuery.expr.match.needsContext; + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { -var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); + // 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, -var risSimple = /^.[^:#\[\.,]*$/; + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } -// 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; - }); + // 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; + } + } - if ( qualifier.nodeType ) { - return jQuery.grep( elements, function( elem ) { - return ( elem === qualifier ) !== not; - }); + // 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 ); + }; - if ( typeof qualifier === "string" ) { - if ( risSimple.test( qualifier ) ) { - return jQuery.filter( qualifier, elements, not ); - } + function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; + } - qualifier = jQuery.filter( qualifier, elements ); - } + function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; - return jQuery.grep( elements, function( elem ) { - return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; - }); -} + return combinator.first ? -jQuery.filter = function( expr, elems, not ) { - var elem = elems[ 0 ]; + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator 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 ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || + ( outerCache[ elem.uniqueID ] = {} ); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( ( oldCache = uniqueCache[ key ] ) && + 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 + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { + return true; + } + } + } + } + } + return false; + }; + } - if ( not ) { - expr = ":not(" + expr + ")"; - } + 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 ]; + } - 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; - })); -}; + function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[ i ], results ); + } + return results; + } -jQuery.fn.extend({ - find: function( selector ) { - var i, - ret = [], - self = this, - len = self.length; + function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; - 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 < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } } } - }) ); - } - - 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; - } -}); + 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 ); + } -// Initialize a jQuery object + // 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 ); + } + } + } -// A central reference to the root jQuery(document) -var rootjQuery, + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { - // Use the correct document accordingly with window argument (sandbox) - document = window.document, + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { - // 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-]*))$/, + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } - init = jQuery.fn.init = function( selector, context ) { - var match, elem; + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector.charAt(0) === "<" && selector.charAt( 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 ]; + // 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( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; - } else { - match = rquickExpr.exec( selector ); + 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 ); } - // Match html or make sure no context is specified for #id - if ( match && (match[1] || !context) ) { + 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, - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), - // 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 - ) ); + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), + len = elems.length; - // 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 ] ); + if ( outermost ) { - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + outermostContext = context == document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // 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; + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( !context && elem.ownerDocument != document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context || document, 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 ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + 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 ); + } } - return this; + // Cache the compiled function + cached = compilerCache( + selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) + ); - // 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 ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootjQuery.find( selector ); + // 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 only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + 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; } - // Otherwise, we inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; + selector = selector.slice( tokens.shift().value.length ); } - this.context = document; - this.selector = selector; - return this; + // 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; + } + } + } } - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); + // 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, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; + }; - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); +// One-time assignments + +// Sort stability + support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; + +// Support: Chrome 14-35+ +// 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( el ) { + + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; + } ); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx + if ( !assert( function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute( "href" ) === "#"; + } ) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + } ); } - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") + if ( !support.attributes || !assert( function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; + } ) ) { + addHandle( "value", function( elem, _name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + } ); + } - // 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 ); - } +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies + if ( !assert( function( el ) { + return el.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; + } + } ); + } - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } + return Sizzle; - return jQuery.makeArray( selector, this ); - }; + } )( window ); -// Give the init function the jQuery prototype for later instantiation -init.prototype = jQuery.fn; -// Initialize central reference -rootjQuery = jQuery( document ); + jQuery.find = Sizzle; + jQuery.expr = Sizzle.selectors; -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 - }; +// Deprecated + jQuery.expr[ ":" ] = jQuery.expr.pseudos; + jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; + jQuery.text = Sizzle.getText; + jQuery.isXMLDoc = Sizzle.isXML; + jQuery.contains = Sizzle.contains; + jQuery.escapeSelector = Sizzle.escape; -jQuery.extend({ - dir: function( elem, dir, until ) { + + + + var dir = function( elem, dir, until ) { var matched = [], - cur = elem[ dir ]; + truncate = until !== undefined; - while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { - if ( cur.nodeType === 1 ) { - matched.push( cur ); + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); } - cur = cur[dir]; } return matched; - }, + }; + - sibling: function( n, elem ) { - var r = []; + var siblings = function( n, elem ) { + var matched = []; for ( ; n; n = n.nextSibling ) { if ( n.nodeType === 1 && n !== elem ) { - r.push( n ); + matched.push( n ); } } - return r; - } -}); + return matched; + }; -jQuery.fn.extend({ - has: function( target ) { - var i, - targets = jQuery( target, this ), - len = targets.length; - return this.filter(function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, + var rneedsContext = jQuery.expr.match.needsContext; - 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)) ) { + function nodeName( elem, name ) { - matched.push( cur ); - break; - } - } - } + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); - }, + } + var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); - // 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 jQuery.inArray( this[0], jQuery( elem ) ); +// Implement the identical functionality for filter and not + function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); } - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[0] : elem, this ); - }, + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } - add: function( selector, context ) { - return this.pushStack( - jQuery.unique( - jQuery.merge( this.get(), jQuery( selector, context ) ) - ) - ); - }, + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter(selector) - ); - } -}); - -function sibling( cur, dir ) { - do { - cur = cur[ dir ]; - } while ( cur && 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 jQuery.nodeName( elem, "iframe" ) ? - elem.contentDocument || elem.contentWindow.document : - jQuery.merge( [], elem.childNodes ); + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var ret = jQuery.map( this, fn, until ); - if ( name.slice( -5 ) !== "Until" ) { - selector = until; + jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; } - if ( selector && typeof selector === "string" ) { - ret = jQuery.filter( selector, ret ); + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; } - if ( this.length > 1 ) { - // Remove duplicates - if ( !guaranteedUnique[ name ] ) { - ret = jQuery.unique( ret ); + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); + }; + + jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + 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; + } + } + } ) ); } - // Reverse order for parents* and prev-derivatives - if ( rparentsprev.test( name ) ) { - ret = ret.reverse(); + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); } + + return len > 1 ? jQuery.uniqueSort( ret ) : 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; } + } ); - return this.pushStack( ret ); - }; -}); -var rnotwhite = (/\S+/g); +// Initialize a jQuery object -// String to Object options format cache -var optionsCache = {}; +// A central reference to the root jQuery(document) + var rootjQuery, -// 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; -} + // 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 <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, -/* - * 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 // Flag to know if list is currently firing - firing, - // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list was already fired - fired, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // First callback to fire (used internally by add and fireWith) - firingStart, - // 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; - } + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; } - firing = false; - if ( list ) { - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } - } else if ( memory ) { - list = []; + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // 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 { - self.disable(); + match = rquickExpr.exec( selector ); } - } - }, - // 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--; + + // 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; + + // Option to run 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 ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); } } } - }); - } - 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 ); + + return this; + + // HANDLE: $(#id) } else { - fire( args ); + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).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[ 0 ] = selector; + this.length = 1; 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; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); } - }; - return self; -}; + return jQuery.makeArray( selector, this ); + }; +// Give the init function the jQuery prototype for later instantiation + init.prototype = jQuery.fn; -jQuery.extend({ +// Initialize central reference + rootjQuery = jQuery( document ); - 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; + var rparentsprev = /^(?:parents|prev(?:Until|All))/, - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 3 ]; + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; - // promise[ done | fail | progress ] = list.add - promise[ tuple[1] ] = list.add; + jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; - // Handle state - if ( stateString ) { - list.add(function() { - // state = [ resolved | rejected ] - state = stateString; + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, - // [ reject_list | resolve_list ].disable; progress_list.lock - }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); - } + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); - // 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; - }); + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { - // Make the deferred a promise - promise.promise( deferred ); + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { - // All done! - return deferred; - }, + matched.push( cur ); + break; + } + } + } + } - // Deferred helper - when: function( subordinate /* , ..., subordinateN */ ) { - var i = 0, - resolveValues = slice.call( arguments ), - length = resolveValues.length, + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, - // the count of uncompleted subordinates - remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + // Determine the position of an element within the set + index: function( elem ) { - // the master Deferred. If resolveValues consist of only a single Deferred, just use that. - deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } - // 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 ); + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } - } else if ( !(--remaining) ) { - deferred.resolveWith( contexts, values ); - } - }; - }, + // Locate the position of the desired element + return indexOf.call( this, - progressValues, progressContexts, resolveContexts; + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, - // 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; - } - } - } + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, - // if we're not waiting on anything, resolve the master - if ( !remaining ) { - deferred.resolveWith( resolveContexts, resolveValues ); + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); } + } ); - return deferred.promise(); + function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; } -}); - - -// The deferred used on DOM ready -var readyList; -jQuery.fn.ready = function( fn ) { - // Add the callback - jQuery.ready.promise().done( fn ); + jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, _i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, _i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, _i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( elem.contentDocument != null && - return this; -}; + // Support: IE 11+ + // elements with no `data` attribute has an object + // `contentDocument` with a `null` prototype. + getProto( elem.contentDocument ) ) { -jQuery.extend({ - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, + return elem.contentDocument; + } - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); + return jQuery.merge( [], elem.childNodes ); } - }, + }, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); - // Handle when the DOM is ready - ready: function( wait ) { + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( jQuery.ready ); - } + if ( this.length > 1 ) { - // Remember that the DOM is ready - jQuery.isReady = true; + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); + return this.pushStack( matched ); + }; + } ); + var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); - // Trigger any bound ready events - if ( jQuery.fn.triggerHandler ) { - jQuery( document ).triggerHandler( "ready" ); - jQuery( document ).off( "ready" ); - } - } -}); -/** - * Clean-up method for dom ready events - */ -function detach() { - if ( document.addEventListener ) { - document.removeEventListener( "DOMContentLoaded", completed, false ); - window.removeEventListener( "load", completed, false ); - } else { - document.detachEvent( "onreadystatechange", completed ); - window.detachEvent( "onload", completed ); +// Convert String-formatted options into Object-formatted ones + function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; } -} -/** - * The ready event handler and self cleanup method + /* + * 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 + * */ -function completed() { - // readyState === "complete" is good enough for us to call the dom ready in oldIE - if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { - detach(); - jQuery.ready(); - } -} + jQuery.Callbacks = function( options ) { -jQuery.ready.promise = function( obj ) { - if ( !readyList ) { + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); - readyList = jQuery.Deferred(); + var // Flag to know if list is currently firing + firing, - // 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 ); + // Last fire value for non-forgettable lists + memory, - // Standards-based browsers support DOMContentLoaded - } else if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed, false ); + // Flag to know if list was already fired + fired, - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed, false ); + // Flag to prevent firing + locked, - // If IE event model is used - } else { - // Ensure firing before onload, maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", completed ); + // Actual callback list + list = [], - // A fallback to window.onload, that will always work - window.attachEvent( "onload", completed ); + // Queue of execution data for repeatable lists + queue = [], - // If IE and not a frame - // continually check to see if the document is ready - var top = false; + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, - try { - top = window.frameElement == null && document.documentElement; - } catch(e) {} - - if ( top && top.doScroll ) { - (function doScrollCheck() { - if ( !jQuery.isReady ) { - - try { - // Use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - top.doScroll("left"); - } catch(e) { - return setTimeout( doScrollCheck, 50 ); - } + // Fire callbacks + fire = function() { - // detach all dom ready events - detach(); + // Enforce single-firing + locked = locked || options.once; - // and execute any waiting functions - jQuery.ready(); - } - })(); - } - } - } - return readyList.promise( obj ); -}; + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } -var strundefined = typeof undefined; + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + firing = false; + // Clean up if we're done firing for good + if ( locked ) { -// Support: IE<9 -// Iteration over object's inherited properties before its own -var i; -for ( i in jQuery( support ) ) { - break; -} -support.ownLast = i !== "0"; - -// Note: most support tests are defined in their respective modules. -// false until the test is run -support.inlineBlockNeedsLayout = false; - -// Execute ASAP in case we need to set body.style.zoom -jQuery(function() { - // Minified: var a,b,c,d - var val, div, body, container; - - body = document.getElementsByTagName( "body" )[ 0 ]; - if ( !body || !body.style ) { - // Return for frameset docs that don't have a body - return; - } + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; - // Setup - div = document.createElement( "div" ); - container = document.createElement( "div" ); - container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; - body.appendChild( container ).appendChild( div ); - - if ( typeof div.style.zoom !== strundefined ) { - // Support: IE<8 - // Check if natively block-level elements act like inline-block - // elements when setting their display to 'inline' and giving - // them layout - div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1"; - - support.inlineBlockNeedsLayout = val = div.offsetWidth === 3; - if ( val ) { - // Prevent IE 6 from affecting layout for positioned elements #11048 - // Prevent IE from shrinking the body in IE 7 mode #12869 - // Support: IE<8 - body.style.zoom = 1; - } - } + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, - body.removeChild( container ); -}); + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { -(function() { - var div = document.createElement( "div" ); + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); - // Execute the test only if not already executed in another module. - if (support.deleteExpando == null) { - // Support: IE<9 - support.deleteExpando = true; - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; - } - } + if ( memory && !firing ) { + fire(); + } + } + return this; + }, - // Null elements to avoid leaks in IE. - div = null; -})(); + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, -/** - * Determines whether an object can have data - */ -jQuery.acceptData = function( elem ) { - var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ], - nodeType = +elem.nodeType || 1; + // 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.length > 0; + }, - // Do not set data on non-element DOM nodes because it will not be cleared (#8335). - return nodeType !== 1 && nodeType !== 9 ? - false : + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, - // Nodes accept data unless otherwise specified; rejection can be conditional - !noData || noData !== true && elem.getAttribute("classid") === noData; -}; + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, -var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - rmultiDash = /([A-Z])/g; + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, -function dataAttr( elem, key, data ) { - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, - var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; - data = elem.getAttribute( name ); + return self; + }; - 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 - jQuery.data( elem, key, data ); - } else { - data = undefined; - } + function Identity( v ) { + return v; + } + function Thrower( ex ) { + throw ex; } - return data; -} - -// checks a cache object for emptiness -function isEmptyDataObject( obj ) { - var name; - for ( name in obj ) { + function adoptValue( value, resolve, reject, noValue ) { + var method; - // if the public data object is empty, the private is still empty - if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { - continue; - } - if ( name !== "toJSON" ) { - return false; - } - } + try { - return true; -} + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); -function internalData( elem, name, data, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); - var ret, thisCache, - internalKey = jQuery.expando, + // Other non-thenables + } else { - // We have to handle DOM nodes and JS objects differently because IE6-7 - // can't GC object references properly across the DOM-JS boundary - isNode = elem.nodeType, + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } + } + + jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( _i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { - // Only DOM nodes need the global jQuery cache; JS object data is - // attached directly to the object so GC can occur automatically - cache = isNode ? jQuery.cache : elem, + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } - // Only defining an ID for JS objects if its cache already exists allows - // the code to shortcut on the same path as a DOM node with no cache - id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).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 = {}; - // Avoid doing any more work than we need to when trying to get data on an - // object that has no data at all - if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { - return; - } + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; - if ( !id ) { - // Only DOM nodes need a new unique ID for each element since their data - // ends up in the global cache - if ( isNode ) { - id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++; - } else { - id = internalKey; - } - } + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; - if ( !cache[ id ] ) { - // Avoid exposing jQuery metadata on plain JS objects when the object - // is serialized using JSON.stringify - cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; - } + // Handle state + if ( stateString ) { + list.add( + function() { - // An object can be passed to jQuery.data instead of a key/value pair; this gets - // shallow copied over onto the existing cache - if ( typeof name === "object" || typeof name === "function" ) { - if ( pvt ) { - cache[ id ] = jQuery.extend( cache[ id ], name ); - } else { - cache[ id ].data = jQuery.extend( cache[ id ].data, name ); - } - } + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, - thisCache = cache[ id ]; + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, - // jQuery data() is stored in a separate object inside the object's internal data - // cache in order to avoid key collisions between internal data and user-defined - // data. - if ( !pvt ) { - if ( !thisCache.data ) { - thisCache.data = {}; - } + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, - thisCache = thisCache.data; - } + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, - if ( data !== undefined ) { - thisCache[ jQuery.camelCase( name ) ] = data; - } + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } - // Check for both converted-to-camel and non-converted data property names - // If a data property was specified - if ( typeof name === "string" ) { + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); - // First Try to find as-is property data - ret = thisCache[ name ]; + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; - // Test for null|undefined property data - if ( ret == null ) { + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); - // Try to find the camelCased property - ret = thisCache[ jQuery.camelCase( name ) ]; - } - } else { - ret = thisCache; - } + // Make the deferred a promise + promise.promise( deferred ); - return ret; -} + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } -function internalRemoveData( elem, name, pvt ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } + // All done! + return deferred; + }, - var thisCache, i, - isNode = elem.nodeType, + // Deferred helper + when: function( singleValue ) { + var - // See jQuery.data for more information - cache = isNode ? jQuery.cache : elem, - id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + // count of uncompleted subordinates + remaining = arguments.length, - // If there is already no cache entry for this object, there is no - // purpose in continuing - if ( !cache[ id ] ) { - return; - } + // count of unprocessed arguments + i = remaining, - if ( name ) { + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), - thisCache = pvt ? cache[ id ] : cache[ id ].data; + // the primary Deferred + primary = jQuery.Deferred(), - if ( thisCache ) { + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + primary.resolveWith( resolveContexts, resolveValues ); + } + }; + }; - // Support array or space separated string names for data keys - if ( !jQuery.isArray( name ) ) { + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject, + !remaining ); - // try the string as a key before any manipulation - if ( name in thisCache ) { - name = [ name ]; - } else { + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( primary.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { - // split the camel cased version by spaces unless a key with the spaces exists - name = jQuery.camelCase( name ); - if ( name in thisCache ) { - name = [ name ]; - } else { - name = name.split(" "); - } + return primary.then(); } - } else { - // 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 = name.concat( jQuery.map( name, jQuery.camelCase ) ); } - i = name.length; + // Multiple arguments are aggregated like Promise.all array elements while ( i-- ) { - delete thisCache[ name[i] ]; + adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject ); } - // If there is no data left in the cache, we want to continue - // and let the cache object itself get destroyed - if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { - return; - } + return primary.promise(); } - } + } ); - // See jQuery.data for more information - if ( !pvt ) { - delete cache[ id ].data; - // Don't destroy the parent cache unless the internal data object - // had been the only thing left in it - if ( !isEmptyDataObject( cache[ id ] ) ) { - return; - } - } +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. + var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; - // Destroy the cache - if ( isNode ) { - jQuery.cleanData( [ elem ], true ); + jQuery.Deferred.exceptionHook = function( error, stack ) { - // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) - /* jshint eqeqeq: false */ - } else if ( support.deleteExpando || cache != cache.window ) { - /* jshint eqeqeq: true */ - delete cache[ id ]; + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } + }; - // When all else fails, null - } else { - cache[ id ] = null; - } -} - -jQuery.extend({ - cache: {}, - - // The following elements (space-suffixed to avoid Object.prototype collisions) - // throw uncatchable exceptions if you attempt to set expando properties - noData: { - "applet ": true, - "embed ": true, - // ...but Flash objects (which have this classid) *can* handle expandos - "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" - }, - - hasData: function( elem ) { - elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; - return !!elem && !isEmptyDataObject( elem ); - }, - - data: function( elem, name, data ) { - return internalData( elem, name, data ); - }, - - removeData: function( elem, name ) { - return internalRemoveData( elem, name ); - }, - - // For internal use only. - _data: function( elem, name, data ) { - return internalData( elem, name, data, true ); - }, - - _removeData: function( elem, name ) { - return internalRemoveData( elem, name, true ); - } -}); -jQuery.fn.extend({ - data: function( key, value ) { - var i, name, data, - elem = this[0], - attrs = elem && elem.attributes; - // Special expections of .data basically thwart jQuery.access, - // so implement the relevant behavior ourselves - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = jQuery.data( elem ); + jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); + }; - if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { - 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 ] ); - } - } - } - jQuery._data( elem, "parsedAttrs", true ); - } - } - return data; - } - // Sets multiple values - if ( typeof key === "object" ) { - return this.each(function() { - jQuery.data( this, key ); - }); - } +// The deferred used on DOM ready + var readyList = jQuery.Deferred(); - return arguments.length > 1 ? + jQuery.fn.ready = function( fn ) { - // Sets one value - this.each(function() { - jQuery.data( this, key, value ); - }) : - - // Gets one value - // Try to fetch any internally stored data first - elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined; - }, - - removeData: function( key ) { - return this.each(function() { - jQuery.removeData( this, key ); - }); - } -}); + readyList + .then( fn ) + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); -jQuery.extend({ - queue: function( elem, type, data ) { - var queue; + return this; + }; - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = jQuery._data( elem, type ); + jQuery.extend( { - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || jQuery.isArray(data) ) { - queue = jQuery._data( elem, type, jQuery.makeArray(data) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, - dequeue: function( elem, type ) { - type = type || "fx"; + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; + // Handle when the DOM is ready + ready: function( wait ) { - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } - if ( fn ) { + // Remember that the DOM is ready + jQuery.isReady = true; - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; } - // clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); } + } ); - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, + jQuery.ready.then = readyList.then; - // not intended for public consumption - generates a queueHooks object, or returns the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return jQuery._data( elem, key ) || jQuery._data( elem, key, { - empty: jQuery.Callbacks("once memory").add(function() { - jQuery._removeData( elem, type + "queue" ); - jQuery._removeData( elem, key ); - }) - }); +// The ready event handler and self cleanup method + function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); } -}); - -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 ); +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon + if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { - // ensure a hooks for this queue - jQuery._queueHooks( this, type ); + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); - 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 ] ); - } - }; + } else { - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); - while ( i-- ) { - tmp = jQuery._data( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); } -}); -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 ); - }; // 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, - length = elems.length, - bulk = key == null; + var 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 many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; - // Sets one value - } else if ( value !== undefined ) { - chainable = true; + if ( !isFunction( value ) ) { + raw = true; + } - if ( !jQuery.isFunction( value ) ) { - raw = true; - } + if ( bulk ) { - if ( bulk ) { - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; + // 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 ); - }; + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, _key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } } - } - if ( fn ) { - for ( ; i < length; i++ ) { - fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } } } - } - return chainable ? - elems : + if ( chainable ) { + return elems; + } // Gets - bulk ? - fn.call( elems ) : - length ? fn( elems[0], key ) : emptyGet; -}; -var rcheckableType = (/^(?:checkbox|radio)$/i); - - - -(function() { - // Minified: var a,b,c - var input = document.createElement( "input" ), - div = document.createElement( "div" ), - fragment = document.createDocumentFragment(); - - // Setup - div.innerHTML = "
    a"; - - // IE strips leading whitespace when .innerHTML is used - support.leadingWhitespace = div.firstChild.nodeType === 3; - - // Make sure that tbody elements aren't automatically inserted - // IE will insert them into empty tables - support.tbody = !div.getElementsByTagName( "tbody" ).length; - - // Make sure that link elements get serialized correctly by innerHTML - // This requires a wrapper element in IE - support.htmlSerialize = !!div.getElementsByTagName( "link" ).length; - - // Makes sure cloning an html5 element does not cause problems - // Where outerHTML is undefined, this still works - support.html5Clone = - document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav>"; - - // Check if a disconnected checkbox will retain its checked - // value of true after appended to the DOM (IE6/7) - input.type = "checkbox"; - input.checked = true; - fragment.appendChild( input ); - support.appendChecked = input.checked; - - // Make sure textarea (and checkbox) defaultValue is properly cloned - // Support: IE6-IE11+ - div.innerHTML = ""; - support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; - - // #11217 - WebKit loses check when the name is after the checked attribute - fragment.appendChild( div ); - div.innerHTML = ""; - - // 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; - - // Support: IE<9 - // Opera does not clone events (and typeof div.attachEvent === undefined). - // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() - support.noCloneEvent = true; - if ( div.attachEvent ) { - div.attachEvent( "onclick", function() { - support.noCloneEvent = false; - }); - - div.cloneNode( true ).click(); - } - - // Execute the test only if not already executed in another module. - if (support.deleteExpando == null) { - // Support: IE<9 - support.deleteExpando = true; - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; + if ( bulk ) { + return fn.call( elems ); } - } -})(); + return len ? fn( elems[ 0 ], key ) : emptyGet; + }; -(function() { - var i, eventName, - div = document.createElement( "div" ); - // Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event) - for ( i in { submit: true, change: true, focusin: true }) { - eventName = "on" + i; +// Matches dashed string for camelizing + var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; - if ( !(support[ i + "Bubbles" ] = eventName in window) ) { - // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) - div.setAttribute( eventName, "t" ); - support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false; - } +// Used by camelCase as callback to replace() + function fcamelCase( _all, letter ) { + return letter.toUpperCase(); } - // Null elements to avoid leaks in IE. - div = null; -})(); +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) + function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + } + var acceptData = function( owner ) { + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); + }; -var rformElems = /^(?:input|select|textarea)$/i, - 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 ) { } -} + function Data() { + this.expando = jQuery.expando + Data.uid++; + } -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { + Data.uid = 1; - global: {}, + Data.prototype = { - add: function( elem, types, handler, data, selector ) { - var tmp, events, t, handleObjIn, - special, eventHandle, handleObj, - handlers, type, namespaces, origType, - elemData = jQuery._data( elem ); + cache: function( owner ) { - // Don't attach events to noData or text/comment nodes (but allow plain objects) - if ( !elemData ) { - return; - } + // Check if the owner object already has a cache + var value = owner[ this.expando ]; - // 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; - } + // If not, create one + if ( !value ) { + value = {}; - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { - // 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 && (!e || jQuery.event.triggered !== e.type) ? - jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : - undefined; - }; - // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events - eventHandle.elem = elem; - } + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); - // 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(); + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // 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 ) ) { + + return this.get( owner, 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, + cache = owner[ this.expando ]; - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; + if ( cache === undefined ) { + return; + } - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; + if ( key !== undefined ) { - // 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 ); + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { - // Init the event handler queue if we're the first - if ( !(handlers = events[ type ]) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); - // Only use addEventListener/attachEvent if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - // Bind the global event handler to the element - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, eventHandle ); - } + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; } } - if ( special.add ) { - special.add.call( elem, handleObj ); + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; } } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } + }; + var dataPriv = new Data(); + + var dataUser = 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 getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; + } + + 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, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); } else { - handlers.push( handleObj ); + data = undefined; } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; } + return data; + } - // Nullify elem to prevent memory leaks in IE - elem = null; - }, + jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - var j, handleObj, tmp, - origCount, t, events, - special, handlers, type, - namespaces, origType, - elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, - if ( !elemData || !(events = elemData.events) ) { - return; - } + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, - // 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(); + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, - // 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; - } + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } + } ); - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); - 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 ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); } } - } - - // 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 ]; + return data; } - } - - // Remove the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - delete elemData.handle; - // removeData also checks for emptiness and clears the expando if empty - // so use it instead of delete - jQuery._removeData( elem, "events" ); - } - }, + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } - trigger: function( event, data, elem, onlyHandlers ) { - var handle, ontype, cur, - bubbleType, special, tmp, i, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + return access( this, function( value ) { + var data; - cur = tmp = elem = elem || document; + // 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 ) { - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } - 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; + // We tried really hard, but the data doesn't exist. + return; + } - // 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 ); + // Set the data... + this.each( function() { - // 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; + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); } + } ); - // 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; - } + jQuery.extend( { + queue: function( elem, type, data ) { + var queue; - // 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 ) ) { + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.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 ); + }; - // 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 ); + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; } - } - // Fire handlers on the event path - i = 0; - while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + if ( fn ) { - event.type = i > 1 ? - bubbleType : - special.bindType || type; + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } - // jQuery handler - handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); } - // 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(); - } + if ( !startLength && hooks ) { + hooks.empty.fire(); } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); } - event.type = type; + } ); - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { + jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; - if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && - jQuery.acceptData( elem ) ) { + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } - // Call a native DOM method on the target with the same name name as the event. - // Can't use an .isFunction() check here because IE6/7 fails that test. - // Don't do default actions on window, that's where global variables be (#6170) - if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); - if ( tmp ) { - elem[ ontype ] = null; - } + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - try { - elem[ type ](); - } catch ( e ) { - // IE<9 dies on focus/blur to hidden element (#1486,#12518) - // only reproducible on winXP IE8 native, not IE9 in IE8 mode + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); } - jQuery.event.triggered = undefined; + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, - if ( tmp ) { - elem[ ontype ] = tmp; + // 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 = dataPriv.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; - return event.result; - }, + var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); - dispatch: function( event ) { - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( event ); + var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; - var i, ret, handleObj, matched, j, - handlerQueue = [], - args = slice.call( arguments ), - handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; + var documentElement = document.documentElement; - // 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 ); + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } + var isHiddenWithinTree = function( elem, el ) { - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; - j = 0; - while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && - // 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 ) ) { + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && - event.handleObj = handleObj; - event.data = handleObj.data; + jQuery.css( elem, "display" ) === "none"; + }; - 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 ); - } + function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), - return event.result; - }, + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); - handlers: function( event, handlers ) { - var sel, handleObj, matches, i, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { - // 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") ) { + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; - /* jshint eqeqeq: false */ - for ( ; cur != this; cur = cur.parentNode || this ) { - /* jshint eqeqeq: true */ + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; - // Don't check non-elements (#13208) - // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { - matches = []; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; - // Don't conflict with Object.prototype properties (#13203) - sel = handleObj.selector + " "; + while ( maxIterations-- ) { - 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 }); - } + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; } - } - } + initialInUnit = initialInUnit / scale; - // Add the remaining (directly-bound) handlers - if ( delegateCount < handlers.length ) { - handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); - } + } - return handlerQueue; - }, + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); - fix: function( event ) { - if ( event[ jQuery.expando ] ) { - return event; + // Make sure we update the tween properties later on + valueParts = valueParts || []; } - // 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 ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; - if ( !fixHook ) { - this.fixHooks[ type ] = fixHook = - rmouseEvent.test( type ) ? this.mouseHooks : - rkeyEvent.test( type ) ? this.keyHooks : - {}; + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } } - copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + return adjusted; + } - event = new jQuery.Event( originalEvent ); - i = copy.length; - while ( i-- ) { - prop = copy[ i ]; - event[ prop ] = originalEvent[ prop ]; - } + var defaultDisplayMap = {}; - // Support: IE<9 - // Fix target property (#1925) - if ( !event.target ) { - event.target = originalEvent.srcElement || document; - } + function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; - // Support: Chrome 23+, Safari? - // Target should not be a text node (#504, #13143) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; + if ( display ) { + return display; } - // Support: IE<9 - // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) - event.metaKey = !!event.metaKey; - - return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; - }, - - // 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 ) { + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); - // Add which for key events - if ( event.which == null ) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } + temp.parentNode.removeChild( temp ); - return event; + if ( display === "none" ) { + display = "block"; } - }, - - mouseHooks: { - props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), - filter: function( event, original ) { - var body, eventDoc, doc, - button = original.button, - fromElement = original.fromElement; + defaultDisplayMap[ nodeName ] = display; - // 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 ); - } + return display; + } - // Add relatedTarget, if necessary - if ( !event.relatedTarget && fromElement ) { - event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; - } + function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; - // 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 ) ) ); + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; } - return event; - } - }, + display = elem.style.display; + if ( show ) { - 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 ) { - try { - this.focus(); - return false; - } catch ( e ) { - // Support: IE<9 - // If we error on focus to hidden element (#1486, #12518), - // let .trigger() run the handlers + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; } } - }, - 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 ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { - this.click(); - return false; + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); } - }, - - // For cross-browser consistency, don't fire native .click() on links - _default: function( event ) { - return jQuery.nodeName( event.target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { + } else { + if ( display !== "none" ) { + values[ index ] = "none"; - // 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; + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); } } } - }, - 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: {} + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; } - ); - if ( bubble ) { - jQuery.event.trigger( e, null, elem ); - } else { - jQuery.event.dispatch.call( elem, e ); - } - if ( e.isDefaultPrevented() ) { - event.preventDefault(); } + + return elements; } -}; -jQuery.removeEvent = document.removeEventListener ? - function( elem, type, handle ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); + jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); } - } : - function( elem, type, handle ) { - var name = "on" + type; + } ); + var rcheckableType = ( /^(?:checkbox|radio)$/i ); - if ( elem.detachEvent ) { + var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); - // #8545, #7054, preventing memory leaks for custom events in IE6-8 - // detachEvent needed property on element, by name of that event, to properly expose it to GC - if ( typeof elem[ name ] === strundefined ) { - elem[ name ] = null; - } + var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); - elem.detachEvent( name, handle ); - } - }; -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: IE < 9, Android < 4.0 - src.returnValue === false ? - returnTrue : - returnFalse; - - // Event type - } else { - this.type = src; - } + ( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || jQuery.now(); + div.appendChild( input ); - // Mark it as fixed - this[ jQuery.expando ] = true; -}; + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; -// 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, + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; - preventDefault: function() { - var e = this.originalEvent; + // Support: IE <=9 only + // IE <=9 replaces "; + support.option = !!div.lastChild; + } )(); - this.isDefaultPrevented = returnTrue; - if ( !e ) { - return; - } - // If preventDefault exists, run it on the original event - if ( e.preventDefault ) { - e.preventDefault(); +// We have to close these tags to support XHTML (#13200) + var wrapMap = { + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
    " ], + col: [ 2, "", "
    " ], + tr: [ 2, "", "
    " ], + td: [ 3, "", "
    " ], + + _default: [ 0, "", "" ] + }; + + wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; + wrapMap.th = wrapMap.td; + +// Support: IE <=9 only + if ( !support.option ) { + wrapMap.optgroup = wrapMap.option = [ 1, "" ]; + } + + + function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); - // Support: IE - // Otherwise set the returnValue property of the original event to false } else { - e.returnValue = false; + ret = []; } - }, - stopPropagation: function() { - var e = this.originalEvent; - this.isPropagationStopped = returnTrue; - if ( !e ) { - return; - } - // If stopPropagation exists, run it on the original event - if ( e.stopPropagation ) { - e.stopPropagation(); + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); } - // Support: IE - // Set the cancelBubble property of the original event to true - e.cancelBubble = true; - }, - stopImmediatePropagation: function() { - var e = this.originalEvent; + return ret; + } + - this.isImmediatePropagationStopped = returnTrue; +// Mark scripts as having already been evaluated + function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; - if ( e && e.stopImmediatePropagation ) { - e.stopImmediatePropagation(); + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); } - - this.stopPropagation(); } -}; -// Create mouseenter/leave events using mouseover/out and event-time checks -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; - } - }; -}); -// IE submit delegation -if ( !support.submitBubbles ) { + var rhtml = /<|&#?\w+;/; - jQuery.event.special.submit = { - setup: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } + function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; - // Lazy-add a submit handler when a descendant form may potentially be submitted - jQuery.event.add( this, "click._submit keypress._submit", function( e ) { - // Node name check avoids a VML-related crash in IE (#9807) - var elem = e.target, - form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; - if ( form && !jQuery._data( form, "submitBubbles" ) ) { - jQuery.event.add( form, "submit._submit", function( event ) { - event._submit_bubble = true; - }); - jQuery._data( form, "submitBubbles", true ); - } - }); - // return undefined since we don't need an event listener - }, + for ( ; i < l; i++ ) { + elem = elems[ i ]; - postDispatch: function( event ) { - // If form was submitted by the user, bubble the event up the tree - if ( event._submit_bubble ) { - delete event._submit_bubble; - if ( this.parentNode && !event.isTrigger ) { - jQuery.event.simulate( "submit", this.parentNode, event, true ); - } - } - }, + if ( elem || elem === 0 ) { - teardown: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } + // Add nodes directly + if ( toType( elem ) === "object" ) { - // Remove delegated handlers; cleanData eventually reaps submit handlers attached above - jQuery.event.remove( this, "._submit" ); - } - }; -} + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); -// IE change delegation and checkbox/radio fix -if ( !support.changeBubbles ) { + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); - jQuery.event.special.change = { + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); - setup: function() { + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; - if ( rformElems.test( this.nodeName ) ) { - // IE doesn't fire change on a check/radio until blur; trigger it on click - // after a propertychange. Eat the blur-change in special.change.handle. - // This still fires onchange a second time for check/radio after blur. - if ( this.type === "checkbox" || this.type === "radio" ) { - jQuery.event.add( this, "propertychange._change", function( event ) { - if ( event.originalEvent.propertyName === "checked" ) { - this._just_changed = true; - } - }); - jQuery.event.add( this, "click._change", function( event ) { - if ( this._just_changed && !event.isTrigger ) { - this._just_changed = false; - } - // Allow triggered, simulated change events (#11500) - jQuery.event.simulate( "change", this, event, true ); - }); + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; } - return false; } - // Delegated event; lazy-add a change handler on descendant inputs - jQuery.event.add( this, "beforeactivate._change", function( e ) { - var elem = e.target; + } - if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { - jQuery.event.add( elem, "change._change", function( event ) { - if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { - jQuery.event.simulate( "change", this.parentNode, event, true ); - } - }); - jQuery._data( elem, "changeBubbles", true ); - } - }); - }, + // Remove wrapper from fragment + fragment.textContent = ""; - handle: function( event ) { - var elem = event.target; + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { - // Swallow native change events from checkbox/radio, we already triggered them above - if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { - return event.handleObj.handler.apply( this, arguments ); + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; } - }, - teardown: function() { - jQuery.event.remove( this, "._change" ); + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } - return !rformElems.test( this.nodeName ); + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } } - }; -} -// Create "bubbling" focus and blur events -if ( !support.focusinBubbles ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + return fragment; + } + - // 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 ); - }; + var rtypenamespace = /^([^.]*)(?:\.(.+)|)/; - jQuery.event.special[ fix ] = { - setup: function() { - var doc = this.ownerDocument || this, - attaches = jQuery._data( doc, fix ); + function returnTrue() { + return true; + } - if ( !attaches ) { - doc.addEventListener( orig, handler, true ); - } - jQuery._data( doc, fix, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this, - attaches = jQuery._data( doc, fix ) - 1; + function returnFalse() { + return false; + } - if ( !attaches ) { - doc.removeEventListener( orig, handler, true ); - jQuery._removeData( doc, fix ); - } else { - jQuery._data( doc, fix, attaches ); - } - } - }; - }); -} +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). + function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); + } -jQuery.fn.extend({ +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 + function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } + } - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var type, origFn; + function on( elem, types, selector, data, fn, 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 ); + on( elem, type, selector, data, types[ type ], one ); } - return this; + return elem; } 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; @@ -5232,722 +5163,913 @@ jQuery.fn.extend({ if ( fn === false ) { fn = returnFalse; } else if ( !fn ) { - return this; + return elem; } 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() { + return elem.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 ); - } + } ); } -}); + /* + * 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: {}, -function createSafeFragment( document ) { - var list = nodeNames.split( "|" ), - safeFrag = document.createDocumentFragment(); + add: function( elem, types, handler, data, selector ) { - if ( safeFrag.createElement ) { - while ( list.length ) { - safeFrag.createElement( - list.pop() - ); - } - } - return safeFrag; -} - -var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + - "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", - rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, - rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), - rleadingWhitespace = /^\s+/, - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, - rtagName = /<([\w:]+)/, - rtbody = /\s*$/g, - - // We have to close these tags to support XHTML (#13200) - wrapMap = { - option: [ 1, "" ], - legend: [ 1, "
    ", "
    " ], - area: [ 1, "", "" ], - param: [ 1, "", "" ], - thead: [ 1, "", "
    " ], - tr: [ 2, "", "
    " ], - col: [ 2, "", "
    " ], - td: [ 3, "", "
    " ], + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); - // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, - // unless wrapped in a div with non-breaking characters in front of it. - _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
    ", "
    " ] - }, - safeFragment = createSafeFragment( document ), - fragmentDiv = safeFragment.appendChild( document.createElement("div") ); - -wrapMap.optgroup = wrapMap.option; -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -function getAll( context, tag ) { - var elems, elem, - i = 0, - found = typeof context.getElementsByTagName !== strundefined ? context.getElementsByTagName( tag || "*" ) : - typeof context.querySelectorAll !== strundefined ? context.querySelectorAll( tag || "*" ) : - undefined; - - if ( !found ) { - for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { - if ( !tag || jQuery.nodeName( elem, tag ) ) { - found.push( elem ); - } else { - jQuery.merge( found, getAll( elem, tag ) ); + // Only attach events to objects that accept data + if ( !acceptData( elem ) ) { + return; } - } - } - return tag === undefined || tag && jQuery.nodeName( context, tag ) ? - jQuery.merge( [ context ], found ) : - found; -} + // 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; + } -// Used in buildFragment, fixes the defaultChecked property -function fixDefaultChecked( elem ) { - if ( rcheckableType.test( elem.type ) ) { - elem.defaultChecked = elem.checked; - } -} + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } -// Support: IE<8 -// Manipulating tables requires a tbody -function manipulationTarget( elem, content ) { - return jQuery.nodeName( elem, "table" ) && - jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } - elem.getElementsByTagName("tbody")[0] || - elem.appendChild( elem.ownerDocument.createElement("tbody") ) : - elem; -} + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = Object.create( null ); + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - elem.type = (jQuery.find.attr( elem, "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; -} + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var elem, - i = 0; - for ( ; (elem = elems[i]) != null; i++ ) { - jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); - } -} + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); -function cloneCopyEvent( src, dest ) { + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } - if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { - return; - } + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; - var type, i, l, - oldData = jQuery._data( src ), - curData = jQuery._data( dest, oldData ), - events = oldData.events; + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; - if ( events ) { - delete curData.handle; - curData.events = {}; + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } + // 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 ); + } + } + } - // make the cloned public data object a copy from the original - if ( curData.data ) { - curData.data = jQuery.extend( {}, curData.data ); - } -} + if ( special.add ) { + special.add.call( elem, handleObj ); -function fixCloneNodeIssues( src, dest ) { - var nodeName, e, data; + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } - // We do not need to do anything for non-Elements - if ( dest.nodeType !== 1 ) { - return; - } + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } - nodeName = dest.nodeName.toLowerCase(); + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } - // IE6-8 copies events bound via attachEvent when using cloneNode. - if ( !support.noCloneEvent && dest[ jQuery.expando ] ) { - data = jQuery._data( dest ); + }, - for ( e in data.events ) { - jQuery.removeEvent( dest, e, data.handle ); - } + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { - // Event data gets referenced instead of copied if the expando gets copied too - dest.removeAttribute( jQuery.expando ); - } + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); - // IE blanks contents when cloning scripts, and tries to evaluate newly-set text - if ( nodeName === "script" && dest.text !== src.text ) { - disableScript( dest ).text = src.text; - restoreScript( dest ); + if ( !elemData || !( events = elemData.events ) ) { + return; + } - // IE6-10 improperly clones children of object elements using classid. - // IE10 throws NoModificationAllowedError if parent is null, #12132. - } else if ( nodeName === "object" ) { - if ( dest.parentNode ) { - dest.outerHTML = src.outerHTML; - } + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - // This path appears unavoidable for IE9. When cloning an object - // element in IE9, the outerHTML strategy above is not sufficient. - // If the src has innerHTML and the destination does not, - // copy the src.innerHTML into the dest.innerHTML. #10324 - if ( support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { - dest.innerHTML = src.innerHTML; - } + // 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; + } - } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - // IE6-8 fails to persist the checked state of a cloned checkbox - // or radio button. Worse, IE6-7 fail to give the cloned element - // a checked appearance if the defaultChecked value isn't also set + 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 ); + } + } + } - dest.defaultChecked = dest.checked = src.checked; + // 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 ) { - // IE6-7 get confused and end up setting the value of a cloned - // checkbox/radio button to an empty string instead of "on" - if ( dest.value !== src.value ) { - dest.value = src.value; - } + jQuery.removeEvent( elem, type, elemData.handle ); + } - // IE6-8 fails to return the selected option to the default selected - // state when cloning options - } else if ( nodeName === "option" ) { - dest.defaultSelected = dest.selected = src.defaultSelected; + delete events[ type ]; + } + } - // IE6-8 fails to set the defaultValue to the correct value when - // cloning other types of input fields - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, -jQuery.extend({ - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var destElements, node, clone, i, srcElements, - inPage = jQuery.contains( elem.ownerDocument, elem ); + dispatch: function( nativeEvent ) { - if ( support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { - clone = elem.cloneNode( true ); + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), - // IE<=8 does not properly clone detached, unknown element nodes - } else { - fragmentDiv.innerHTML = elem.outerHTML; - fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); - } + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( nativeEvent ), - if ( (!support.noCloneEvent || !support.noCloneChecked) && - (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + handlers = ( + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; - // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; - // Fix all IE cloning issues - for ( i = 0; (node = srcElements[i]) != null; ++i ) { - // Ensure that the destination node is not null; Fixes #9587 - if ( destElements[i] ) { - fixCloneNodeIssues( node, destElements[i] ); - } + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; } - } - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); + event.delegateTarget = this; - for ( i = 0; (node = srcElements[i]) != null; i++ ) { - cloneCopyEvent( node, destElements[i] ); - } - } else { - cloneCopyEvent( elem, clone ); + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; } - } - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - destElements = srcElements = node = null; + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; - // Return the cloned set - return clone; - }, + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { - buildFragment: function( elems, context, scripts, selection ) { - var j, elem, contains, - tmp, tag, tbody, wrap, - l = elems.length, + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { - // Ensure a safe fragment - safe = createSafeFragment( context ), + event.handleObj = handleObj; + event.data = handleObj.data; - nodes = [], - i = 0; + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); - for ( ; i < l; i++ ) { - elem = elems[ i ]; + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } - if ( elem || elem === 0 ) { + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } - // Add nodes directly - if ( jQuery.type( elem ) === "object" ) { - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + return event.result; + }, - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } - // Convert html into DOM nodes - } else { - tmp = tmp || safe.appendChild( context.createElement("div") ); + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } - // Deserialize a standard representation - tag = (rtagName.exec( elem ) || [ "", "" ])[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; + return handlerQueue; + }, - tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, - // Descend through wrappers to the right content - j = wrap[0]; - while ( j-- ) { - tmp = tmp.lastChild; - } + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, - // Manually add leading whitespace removed by IE - if ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { - nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); - } + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, - // Remove IE's autoinserted from table fragments - if ( !support.tbody ) { + special: { + load: { - // String was a , *may* have spurious - elem = tag === "table" && !rtbody.test( elem ) ? - tmp.firstChild : + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { - // String was a bare or - wrap[1] === "
    " && !rtbody.test( elem ) ? - tmp : - 0; + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { - j = elem && elem.childNodes.length; - while ( j-- ) { - if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { - elem.removeChild( tbody ); - } - } + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); } - jQuery.merge( nodes, tmp.childNodes ); + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { - // Fix #12392 for WebKit and IE > 9 - tmp.textContent = ""; + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { - // Fix #12392 for oldIE - while ( tmp.firstChild ) { - tmp.removeChild( tmp.firstChild ); + leverageNative( el, "click" ); } - // Remember the top-level container for proper cleanup - tmp = safe.lastChild; + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); } - } - } + }, - // Fix #11356: Clear elements from fragment - if ( tmp ) { - safe.removeChild( tmp ); - } + beforeunload: { + postDispatch: function( event ) { - // Reset defaultChecked for any radios and checkboxes - // about to be appended to the DOM in IE 6/7 (#8060) - if ( !support.appendChecked ) { - jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + // 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; + } + } + } } + }; - i = 0; - while ( (elem = nodes[ i++ ]) ) { +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. + function leverageNative( el, type, expectSync ) { - // #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; + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); } + return; + } - contains = jQuery.contains( elem.ownerDocument, elem ); + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { - // Append to fragment - tmp = getAll( safe.appendChild( elem ), "script" ); + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); - // Preserve script evaluation history - if ( contains ) { - setGlobalEval( tmp ); - } + // Support: Chrome 86+ + // In Chrome, if an element having a focusout handler is blurred by + // clicking outside of it, it invokes the handler synchronously. If + // that handler calls `.remove()` on the element, the data is cleared, + // leaving `result` undefined. We need to guard against this. + return result && result.value; + } - // Capture executables - if ( scripts ) { - j = 0; - while ( (elem = tmp[ j++ ]) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); } } + } ); + } + + jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); } + }; - tmp = null; + jQuery.Event = function( src, props ) { - return safe; - }, + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } - cleanData: function( elems, /* internal */ acceptData ) { - var elem, type, id, data, - i = 0, - internalKey = jQuery.expando, - cache = jQuery.cache, - deleteExpando = support.deleteExpando, - special = jQuery.event.special; + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; - for ( ; (elem = elems[i]) != null; i++ ) { - if ( acceptData || jQuery.acceptData( elem ) ) { + // 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 && - id = elem[ internalKey ]; - data = id && cache[ id ]; + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; - if ( data ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; - // Remove cache only if it was not already removed by jQuery.event.remove - if ( cache[ id ] ) { + // Event type + } else { + this.type = src; + } - delete cache[ id ]; + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } - // IE does not allow us to delete expando properties from nodes, - // nor does it have a removeAttribute function on Document nodes; - // we must handle all of these cases - if ( deleteExpando ) { - delete elem[ internalKey ]; + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); - } else if ( typeof elem.removeAttribute !== strundefined ) { - elem.removeAttribute( internalKey ); + // Mark it as fixed + this[ jQuery.expando ] = true; + }; - } else { - elem[ internalKey ] = null; - } +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html + jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, - deletedIds.push( id ); - } - } + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); } - } - } -}); - -jQuery.fn.extend({ - text: function( value ) { - return access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( 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; + }, + stopPropagation: function() { + var e = this.originalEvent; - for ( ; (elem = elems[i]) != null; i++ ) { + this.isPropagationStopped = returnTrue; - if ( !keepData && elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem ) ); + if ( e && !this.isSimulated ) { + e.stopPropagation(); } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; - if ( elem.parentNode ) { - if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { - setGlobalEval( getAll( elem, "script" ) ); - } - elem.parentNode.removeChild( elem ); + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); } + + this.stopPropagation(); } + }; - return this; - }, +// Includes all common event props including KeyEvent and MouseEvent specific props + jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + which: true + }, jQuery.event.addProp ); + + jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { - empty: function() { - var elem, - i = 0; + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); - for ( ; (elem = this[i]) != null; i++ ) { - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + // Suppress native focus or blur as it's already being fired + // in leverageNative. + _default: function() { + return true; + }, + + delegateType: delegateType + }; + } ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). + 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 mouseenter/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; + } + }; + } ); + + jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, 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" ) { - // Remove any remaining nodes - while ( elem.firstChild ) { - elem.removeChild( elem.firstChild ); + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; } + if ( selector === false || typeof selector === "function" ) { - // If this is a select, ensure that it displays empty (#12336) - // Support: IE<9 - if ( elem.options && jQuery.nodeName( elem, "select" ) ) { - elem.options.length = 0; + // ( types [, fn] ) + fn = selector; + selector = undefined; } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); } + } ); - 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 ); - }); - }, + var - html: function( value ) { - return access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - ( support.htmlSerialize || !rnoshimcache.test( value ) ) && - ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && - !wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) { +// Prefer a tbody over its parent table for containing new rows + function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { - value = value.replace( rxhtmlTag, "<$1>" ); + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } - try { - for (; i < l; i++ ) { - // Remove element nodes and prevent memory leaks - elem = this[i] || {}; - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } + return elem; + } - elem = 0; +// 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 ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } - // If using innerHTML throws an exception, use the fallback method - } catch(e) {} - } + return elem; + } - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, + function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, udataOld, udataCur, events; - replaceWith: function() { - var arg = arguments[ 0 ]; + if ( dest.nodeType !== 1 ) { + return; + } - // Make the changes, replacing each context element with the new content - this.domManip( arguments, function( elem ) { - arg = this.parentNode; + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.get( src ); + events = pdataOld.events; - jQuery.cleanData( getAll( this ) ); + if ( events ) { + dataPriv.remove( dest, "handle events" ); - if ( arg ) { - arg.replaceChild( elem, this ); + 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 ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } + } - // Force removal if there was no new content (e.g., from empty arguments) - return arg && (arg.length || arg.nodeType) ? this : this.remove(); - }, +// Fix IE bugs, see support tests + function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); - detach: function( selector ) { - return this.remove( selector, true ); - }, + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; - domManip: function( args, callback ) { + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } + } + + function domManip( collection, args, callback, ignored ) { // Flatten any nested arrays - args = concat.apply( [], args ); + args = flat( args ); - var first, node, hasScripts, - scripts, doc, fragment, + var fragment, first, scripts, hasScripts, node, doc, i = 0, - l = this.length, - set = this, + l = collection.length, iNoClone = l - 1, - value = args[0], - isFunction = jQuery.isFunction( value ); + value = args[ 0 ], + valueIsFunction = 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() ); + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); } - self.domManip( args, callback ); - }); + domManip( self, args, callback, ignored ); + } ); } if ( l ) { - fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); first = fragment.firstChild; if ( fragment.childNodes.length === 1 ) { fragment = first; } - if ( first ) { + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { 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 + // 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; @@ -5957,11 +6079,14 @@ jQuery.fn.extend({ // Keep references to cloned scripts for later restoration if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( scripts, getAll( node, "script" ) ); } } - callback.call( this[i], node, i ); + callback.call( collection[ i ], node, i ); } if ( hasScripts ) { @@ -5974,206 +6099,507 @@ jQuery.fn.extend({ for ( i = 0; i < hasScripts; i++ ) { node = scripts[ i ]; if ( rscriptType.test( node.type || "" ) && - !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { - if ( node.src ) { // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl ) { - jQuery._evalUrl( node.src ); + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + }, doc ); } } else { - jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); } } } } - - // Fix #11809: Avoid leaking memory - fragment = first = null; } } - return this; + return collection; } -}); - -jQuery.each({ - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - i = 0, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1; - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone(true); - jQuery( insert[i] )[ original ]( elems ); + function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } - // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() - push.apply( ret, elems.get() ); + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } } - return this.pushStack( ret ); - }; -}); + return elem; + } + jQuery.extend( { + htmlPrefilter: function( html ) { + return html; + }, -var iframe, - elemdisplay = {}; + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); -/** - * 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 ), + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { - // getDefaultComputedStyle might be reliably used only on attached element - display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); - // 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" ); + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } - // 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(); + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); - return display; -} + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } -/** - * Try to determine the default display value of an element - * @param {String} nodeName - */ -function defaultDisplay( nodeName ) { - var doc = document, - display = elemdisplay[ nodeName ]; + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } - if ( !display ) { - display = actualDisplay( nodeName, doc ); + // Return the cloned set + return clone; + }, - // If the simple way fails, read from inside an iframe - if ( display === "none" || !display ) { + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; - // Use the already-created iframe if possible - iframe = (iframe || jQuery( "